2

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!

2
  • @SamFisher83 - Your suggestion makes no sense. The use of a string is the correct procedure. Of course the two structures are not even the same size, the C# structure is at least 12 bits larger. Commented Apr 30, 2012 at 17:20
  • @Ramhound I corrected my typo error. Now, device_name is 51 chars long. Is there still a mismatch? Commented May 2, 2012 at 8:33

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

7 Comments

You should also mention TCHAR device_name [20]; vs SizeConst = 51 -- this will cause DeviceID to reside at the wrong offset.
I assumed that was a simple typo but it need to be fixed.
@MartinLiversage - The chances of that error being a typo is unlikely.
@MartinLiversage thanks, but I'm not sure if that's the problem. If I remove the string from my structure and have only integers, it's working fine. On the unmanaged side, I have the address of memory location correct at each iteration. It seems the memory is set correctly on the unmanaged side, but it is somehow(?) lost when I switch back to the managed side.
@mustafa: Strings are different because they are pointers. When returning from the call the marshaller has to follow those pointers and create managed strings from the data pointed to. The marshaller only does that for the first element unless you provide it with some information about the size of the array.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.