Home
Products
Forums
Help
Publish Article

How to marshal structure containing variable length array?

Have you ever run into situation where you have to call into a classic C DLL that returns you an opaque pointer (VOID) that contains a structure containing an array of variable length? For example look at the following structures defined in header file of unmanaged C DLL.

typedef struct _MY_DATA
{
	USHORT	usData;
	UINT	uiSize;
	USHORT	usValues[1];
}MY_DATA, *LPMY_DATA;

typedef struct _MY_CCONTAINER
{
	USHORT usType;
	HANDLE hContainingData;
}MY_CONTAINER,*LPMY_CONTAINER

Second structure MY_CONTAINER has an opaque member hContainingData and this member represents the first structure MY_DATA. The trouble part is usValues member in MY_DATA structure. This is defined as an array of dimension one. The actual size of this array is determined by member uiSize. Now the question is how do we define this structure prototype in our .Net application. If the array was of fixed size then it was easy and we could have defined it the following way (assuming that size of array is 10).

[StructureLayout(LayoutKind.Sequential)]
class MY_DATA
{
	public UInt16 usData;
	public UInt32 uiSize;
	[MarshalAs(UnmanagedType.LPArray, SizeConst=10)]
	public UInt16 [] usValues;
}						
						

Unfortunately we can't define protype like this for an embded array whose size will be known at run time. And .Net framework does not provide any support for these kind of embded arrays. There are few possible solutions for this problem. One could be that you define the prototype assuming the array is of very large size and then at run time uiSize memeber will be used to determine the exact size of array and access the array for those number of elements. The problem with this approach is that it is not possible to pre-detemine size of array. If you assign a very large number, you will end up reserving a lot of memory which will be unused. And then you have to rely on the callee function that it will not try to assign memory beyond the allocated size.

I will demonstrate an approach that does not require you to pre-define the size of array. This approach involves manually walking the memory block returned by unmanaged function, and accessing every variable individually. This apprach requires help from a few Win32 functions that we will call using PInvoke. First we take a look at the unamanged function protype.

BOOL OutStructWithArray(LPVOID inoutStruct);						
						

.Net protype definition for this function will be as follows.

[StructLayout(LayoutKind.Sequential, Pack=2)]
internal class MY_CONTAINER
{
	internal ushort usType;
	internal IntPtr hContainerData;
}						

[DllImport("Unmanaged.dll")]
internal static extern bool OutStructWithArray([In, Out]MY_CONTAINER obMyCont);						
						

Following code snippet demonstrates how each member of the array is accsses by manually walking the memory location returned by unmanaged code.

static void Main(string[] args)
{
	MY_CONTAINER obMyCont = new MY_CONTAINER();
	obMyCont.usType = 12;
	obMyCont.hContainerData = IntPtr.Zero;

	bool bRet = StructsTest.OutStructWithArray(obMyCont);
	if (bRet && obMyCont.hContainerData != IntPtr.Zero)
	{
		//
		// Lock the memory block.
		//

		IntPtr pData = StructsTest.GlobalLock(obMyCont.hContainerData);

		IntPtr pArrayLocation = pData;

		//
		// Read usData1 values.
		//

		UInt16 uiData1 = (UInt16)Marshal.ReadInt16(pData);

		//
		// Read size of the array at offset of uiType memeber variable.
		//

		int iOffset = Marshal.SizeOf(Type.GetType("System.UInt16"));
		Int32 uiSize = Marshal.ReadInt32(pData, iOffset);

		//
		// Allocate buffer to get copy of the array.
		//

		Int16 [] vals = new Int16[uiSize];

		//
		// Advance to the location of the array.
		//
		
		iOffset += Marshal.SizeOf(Type.GetType("System.UInt32"));
		pArrayLocation = (IntPtr)((int)pArrayLocation + iOffset);

		Marshal.Copy(pArrayLocation, vals, 0, uiSize);

		StructsTest.GlobalUnlock(pData);

		StructsTest.GlobalFree(obMyCont.hContainerData);
	}	
}					
						

The attached soource code contains the unmanaged as well as managed projects that demonstrate this approach.

Go Freelance
Home     About us     Contact us    Copyright    Privacy Policy    Return Policy    Advertisers
Copyright © Netomatix