Reputation: 3905
I need to pass an array of a structure from C++ to C#. I have the following code, which first creates a temporary structure, then does memcpy
it to the address of the structure in the C# side. I defined the structure on both sides with the same order of elements.
When I debug I see that the temporary structure is filled correctly, temp_buf (the address of of the target variable) is incremented in the size of the structure at each iteration, and memcpy
does not return any errors.
However, only the first item of the array is set in the C# side.
Here are the definitions on the C# side:
[DllImport(@"MyAwesomeDLL.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?GetDevices@@YAHPAXIPAIPAUDEVICE_S@@@Z", CharSet = CharSet.Auto)]
public static extern Int32 GetCoolDevices(out UInt32 p_deviceCount, out Device_s p_devicesBuf);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Device_s
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public String DeviceName;
public UInt32 DeviceID;
}
Here are the definitions on the C++ side:
#pragma pack (1)
typedef struct
{
TCHAR device_name [51];
UINT32 device_rid;
} DEVICE_S;
int GetDevices (UINT32 *device_count, DEVICE_S *devices_buf)
{
....
while(....)
{
DEVICE_S tmp_device;
memset(&tmp_device, 0, sizeof(DEVICE_S));
tmp_device.device_id = 5;
sprintf( tmp_device.device_name, "a string");
DEVICE_S *temp_buf = &(devices_buf[i]);
size_t mysize = sizeof(DEVICE_S);
memcpy(temp_buf, &tmp_device, mysize);
i++;
getNextDevice();
}
.....
}
And, here is how I call it:
UInt32 MyDecDeviceCnt = 0;
Device_s[] MyDecDevices = new Device_s[10];
int ret = GetCoolDevices(out MyDecDeviceCnt, out MyDecDevices[0]);
Any suggestion is appreciated!
Upvotes: 2
Views: 2507
Reputation: 106926
There is a problem with the API that you have created. You allocate the memory for the array on the managed side (10 elements) but you don't pass the number of elements in the array to the unmanaged side. The marshaller has no way to determine that you want to marshal 10 elements and the unmanaged code don't know the size of the buffer it is writing to.
You can use MarshalAsAttribute.SizeParamIndex to provide information to the marshaller about the size of the array. Also have a look at the article Default Marshaling for Arrays. (In particular the information about C-style arrays.)
Given the nature of your API you could change it so that the memory is allocated on the unmanaged side. You would have to use a safe array or an hglobal to allow the memory to be freed on the managed side.
Upvotes: 2