Thursday, April 23, 2009

Interop with a struct that contains an array of structs

To pass a struct that contains an array of structs from C# to C++ a managed version of both structs must be created. The easiest way to create the managed version of the structs is via the P/Invoke Interop Assistant.

For example, consider the following native structs:

typedef struct MYNAME_t
{
char* first;
char *last;
};

typedef struct MYGROUP_t
{
int count;
MYNAME_t groupMembers[5];
};




MYGROUP_t is a struct that contains 5 MYNAME_t structs. The managed version of these structs follows:


[StructLayout(LayoutKind.Sequential)]
public struct MYNAME_t
{
[MarshalAs(UnmanagedType.LPStr)] public string first;
[MarshalAs(UnmanagedType.LPStr)] public string last;
};

[StructLayout(LayoutKind.Sequential)]
public struct MYGROUP_t
{
public int count;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.Struct)]
public MYNAME_t[] groupMembers;
};




To handle the embedded array of structs the groupMembers field must be marshaled as a ByValArray with the size of the array and array sub type specified.





The following native function takes a MYGROUP_t as a parameter:


INTEROPTEST_API int testStructWithStructArray(MYGROUP group)
{
cout << "in testStructWithStructArray() group.count=" << group.count << endl;

for (int i=0; i < group.count; i++)
{
cout << "member[" << i << "]=" << group.groupMembers[i].first <<
" " << group.groupMembers[i].last << endl;
}
return 42;
}





The native function can be called from managed code as follows:




[DllImport("InteropTest.dll", EntryPoint = "testStructWithStructArray")]
public static extern int testStructWithStructArray(MYGROUP_t group);

...

MYNAME_t[] members = new MYNAME_t[5];

group.groupMembers = members;
group.count = 5;

testStructWithStructArray(group);






NOTE: The native code in this example must be surrounded by

 extern "C" {
...
}


to demangle the names in the exports.

No comments:

Post a Comment