Marshal a C struct containing a variable length array

I would like to combine a C structure with a variable length array back in C #, but so far I can’t get anything better than representing a pointer to a structure and a pointer to a float.

Unmanaged View:

typedef float smpl_t; typedef struct { uint_t length; /**< length of buffer */ smpl_t *data; /**< data vector of length ::fvec_t.length */ } fvec_t; 

Guided View:

 [StructLayout(LayoutKind.Sequential)] public unsafe struct fvec_t1 { public uint length; public float* data; } [DllImport("libaubio-4.dll", EntryPoint = "new_fvec", PreserveSig = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern unsafe fvec_t1* new_fvec1(uint length); 

I would like to have a .NET style array where data will be float[] , but if I changed the structure to the form below, I get I can’t take the address, get the size, or declare a pointer to the managed type in the external function above.

 [StructLayout(LayoutKind.Sequential)] public unsafe struct fvec_t1 { public uint length; public float[] data; } 

Apparently, it is not possible for an array of variable length to sort as-is, is this correct or is there still a way to achieve this

+6
source share
2 answers

the short answer is you cannot marshal an array of variable length as an array, because without knowing the size, the interop sorting service cannot marshal the elements of the array

but if you know the size, it will be as follows:

 int arr[15] 

you can do it like this:

 [MarshalAs(UnmanagedType.LPArray, SizeConst=15)] int[] arr 

if you don't know the length of the array, and that is what you want you can convert it to intprt and handle inptr, but first you need to create 2 structures

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] struct fvec_t1 { public uint whatever; public int[] data; } 

another as below:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] struct fvec_t2{ public uint whatever; } 

create a function to initialize the array as shown below

 private static int[] ReturnIntArray() { int [] myInt = new int[30]; for (int i = 0; i < myInt.length; i++) { myInt[i] = i + 1; } return myInt; } 

create an instance of the first structure

 fvec_t1 instance = new fvec_t1(); instance.whatever=10; instance.data= ReturnIntArray(); 

create an instance of the second structure

 fvec_t2 instance1 = new fvec_t2(); instance1.whatever = instance.whatever 

dynamically allocate space for the fvec_t2 structure with extended space for the data array

 IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(fvec_t2)) + Instance.data.Length); 

Transfer existing values ​​of the fvec_t2 field to the memory space pointed to by ptr

 Marshal.StructureToPtr(instance1, ptr, true); 

Calculate the offset field of the data array, which should be at the end of the fvec_t2 structure

 int offset = Marshal.SizeOf(typeof(fvec_t2)); 

get the memory address of the data array field based on the offset.

 IntPtr address = new IntPtr(ptr.ToInt32() + offset); 

copy data to ptr

 Marshal.Copy(instance.data, 0, address, instance.data.Length); 

make a call

 bool success = dllfunction(ptr); Marshal.FreeHGlobal(ptr); ptr = IntPtr.Zero; 
+6
source

In the above case, I definitely used SizeParamIndex.

 [StructLayout(LayoutKind.Sequential)] public struct fvec_t1 { uint length; [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] float[] data; } 

Good luck.

-1
source

All Articles