Ezi Ezi - 3 months ago 18
C++ Question

How do I marshal a struct that contains a variable-sized array to C#?

How do I marshal this C++ type?

The ABS_DATA structure is used to associate an arbitrarily long data block with the length information. The declared length of the

Data
array is 1, but the actual length is given by the
Length
member.

typedef struct abs_data {
ABS_DWORD Length;
ABS_BYTE Data[ABS_VARLEN];
} ABS_DATA;


I tried the following code, but it's not working. The data variable is always empty and I'm sure it has data in there.

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
public struct abs_data
{
/// ABS_DWORD->unsigned int
public uint Length;

/// ABS_BYTE[1]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 1)]
public string Data;
}

Answer

If the data being saved isn't a string, you don't have to store it in a string. I usually do not marshal to a string unless the original data type was a char*. Otherwise a byte[] should do.

Try:

[MarshalAs(UnmanagedType.ByValArray, SizeConst=[whatever your size is]]
byte[] Data;

If you need to convert this to a string later, use:

System.Text.Encoding.UTF8.GetString(your byte array here). 

Obviously, you need to vary the encoding to what you need, though UTF-8 usually is sufficient.

I see the problem now, you have to marshal a VARIABLE length array. The MarshalAs does not allow this and the array will have to be sent by reference.

If the array length is variable, your byte[] needs to be an IntPtr, so you would use,

IntPtr Data;

Instead of

[MarshalAs(UnmanagedType.ByValArray, SizeConst=[whatever your size is]]
byte[] Data;

You can then use the Marshal class to access the underlying data.

Something like:

uint length = yourABSObject.Length;
byte[] buffer = new byte[length];

Marshal.Copy(buffer, 0, yourABSObject.Data, length);

You may need to clean up your memory when you are finished to avoid a leak, though I suspect the GC will clean it up when yourABSObject goes out of scope. Anyway, here is the cleanup code:

Marshal.FreeHGlobal(yourABSObject.Data);
Comments