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)
{
return;
}
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);
::SysFreeString(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;
StringFromIID
(
guid,
&lpwzGuid
);
if (lpwzGuid)
{
printf ("Record GUID : [%S].\r\n", lpwzGuid);
::CoTaskMemFree((LPVOID)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;
DisplayContentsOfRecordVariant2(managed_udt);
}
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.
Discussion
Trackbacks/Pingbacks
Pingback: Using a VT_RECORD VARIANT in Managed Code Part 1 « limbioliong - September 25, 2011
Pingback: Using a VT_RECORD VARIANT in Managed Code Part 3 « limbioliong - September 29, 2011