you're reading...
.NET/COM/ActiveX Interop, COM

Using a VT_RECORD VARIANT in Managed Code Part 2

1. Introduction.

1.1 In part 1, I passed a native VT_RECORD VARIANT from managed code to unmanaged code. The VARIANT demonstrated in the example code of part 1 was created directly using code (Marshal.AllocHGlobal() and Marshal.GetNativeVariantForObject()).

1.2 Here in part 2, I shall demonstrate a very simple, albeit indirect, way to pass a VT_RECORD VARIANT from managed code to unmanaged code.

1.3 This time, the VARIANT is not created in code but is created by the interop marshaler under the covers.

1.4 We shall continue to use the ManagedUDT structure first defined in part 1. However, a new API (modified from the DisplayContentsOfRecordVariant() API presented in part 1) will be defined and used.

2. API that Displays the Contents of a VT_RECORD VARIANT.

2.1 The code listing below shows an unmanaged function that displays the contents of a VT_RECORD VARIANT. The main difference from its predecessor is that it takes a direct VARIANT structure as parameter instead of a pointer :

__declspec(dllexport) void __stdcall DisplayContentsOfRecordVariant2(/*[in]*/ const VARIANT var)
	if (V_VT(&var) != VT_RECORD)

	IRecordInfo*	pIRecordInfo = V_RECORDINFO(&var);
	ManagedUDT*		pManagedUDT = (ManagedUDT*)(V_RECORD(&var));

	// Display information on the IRecordInfo object assocoated
	// with the ManagedUDT structure.
	if (pIRecordInfo)
		// Obtain the name of the UDT.
		// This should be "ManagedUDT".
		BSTR bstrRecordName = NULL;
		pIRecordInfo -> GetName(&bstrRecordName);
		if (bstrRecordName)
		  printf ("Record Name : [%S].\r\n", bstrRecordName);
		  bstrRecordName = NULL;

		// Obtain the GUID associated with the UDT.
		// This should be the one declared via the
		// GuidAttribute for the ManagedUDT structure,
		// i.e. "BBFE1092-A90C-4b6d-B279-CBA28B9EDDFA".
		GUID guid;
		pIRecordInfo -> GetGuid(&guid);

		// Display the GUID as a string.
		LPOLESTR lpwzGuid = NULL;

		if (lpwzGuid)
		  printf ("Record GUID : [%S].\r\n", lpwzGuid);
		  lpwzGuid = NULL;

	// Display the field values of the ManagedUDT structure
	// contained inside the input VARIANT.
	if (pManagedUDT)
	  printf ("pManagedUDT -> m_str01 : [%S].\r\n", pManagedUDT -> m_str01);
	  printf ("pManagedUDT -> m_int01 : [%d].\r\n", pManagedUDT -> m_int01);

2.2 The C# declaration of this API is listed below :

[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void DisplayContentsOfRecordVariant2([In] [MarshalAs(UnmanagedType.Struct)] Object var);

3. Passing a VT_RECORD VARIANT to an Unmanaged API as a System.Object.

3.1 In this section, I shall demonstrate how to marshal ManagedUDT to the DisplayContentsOfRecordVariant2() API directly as a System.Object type :

private static void DisplayManagedUDTViaSystemObject()
    // Allocate a ManagedUDT structure and fill its member fields.
    ManagedUDT managed_udt = new ManagedUDT();
    managed_udt.m_str01 = "Hello World";
    managed_udt.m_int01 = 100;


3.2 As can be seen, it is incredibly simple. The MarshalAsAttribute used with the UnmanagedType.Struct argument indicates to the interop marshaler to marshal the ManagedUDT structure as a VARIANT. And because ManagedUDT is a structure, the VARIANT will be of VT_RECORD type.

3.3 With this marshaling instruction specificed, all the hard work is done by the interop marshaler. This includes :

  • Allocating memory space for a native VARIANT earmarked to be of VT_RECORD type.
  • Obtaining the ITypeInfo associated with the ManagedUDT structure.
  • Calling the GetRecordInfoFromTypeInfo() API to obtain the IRecordInfo interface pointer associated with the ITypeInfo of the ManagedUDT structure.
  • Allocating an unmanaged ManagedUDT structure (this is most likely performed by using the IRecordInfo::RecordCreate() method).
  • Filling in the field values of the unmanaged ManagedUDT structure using values from the managed ManagedUDT structure (this is likely performed by using Marshal.StructureToPtr()).
  • Calling VariantClear() on the native VARIANT.

4. In Conclusion.

4.1 In this part 2, I have demonstrated default marshaling of a managed structure as a System.Object.

4.2 This used to be impossible in the first version of the .NET framework version. But time has passed and this is now possible.

4.3 However, time has passed and circa 2011, it is currently still not possible to receive a VT_RECORD VARIANT from unmanaged code as an out System.Object parameter.

4.4 It is also not possible to convert the contents of a VT_RECORD VARIANT to a managed structure using Marshal.GetObjectForNativeVariant().

4.5 From part 3 of this series of blogs, I shall start to address these issues and endeavour to finally receive a VT_RECORD VARIANT from unmanaged code.

4.6 In part 3, I shall be using the VariantStructGeneric structure that I defined in Defining a VARIANT Structure in Managed Code Part 2.

4.7 Return to Part 1.

4.8 Proceed to Part 3.



About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.



  1. Pingback: Using a VT_RECORD VARIANT in Managed Code Part 1 « limbioliong - September 25, 2011

  2. Pingback: Using a VT_RECORD VARIANT in Managed Code Part 3 « limbioliong - September 29, 2011

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: