I would like to marshal a C struct with a variable-length array back to C# but so far I can't get anything better than a pointer-to-struct representation and a pointer to float.
Unmanaged representation:
typedef float smpl_t;
typedef struct {
uint_t length; /**< length of buffer */
smpl_t *data; /**< data vector of length ::fvec_t.length */
} fvec_t;
Managed representation:
[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);
What I would like is to have a .NET style array, where data
would be float[]
but if I do change the struct to the form below I do get Cannot take the address of, get the size of, or declare a pointer to a 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 to a have a variable-length array marshalled back as-is, is this correct or is it there still a way to achieve this ?
short answer you can't marshal variable length array as an array , because Without knowing the size, the interop marshalling service cannot marshal the array elements
but if you know the size it will be like below:
int arr[15]
you will be able to marshal it like this:
[MarshalAs(UnmanagedType.LPArray, SizeConst=15)] int[] arr
if you don't know the length of the array and this is what you want you can convert it to intprt and deal with inptr but first you need to create 2 structs
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct fvec_t1
{
public uint whatever;
public int[] data;
}
the other one like below:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct fvec_t2{
public uint whatever;
}
create a function to initialize the array like below
private static int[] ReturnIntArray()
{
int [] myInt = new int[30];
for (int i = 0; i < myInt.length; i++)
{
myInt[i] = i + 1;
}
return myInt;
}
instantiate the first struct
fvec_t1 instance = new fvec_t1();
instance.whatever=10;
instance.data= ReturnIntArray();
instantiate the second struct
fvec_t2 instance1 = new fvec_t2();
instance1.whatever = instance.whatever
dynamically allocate space for fvec_t2 struct with extended space for data array
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(fvec_t2)) + Instance.data.Length);
Transfer the existing field values of fvec_t2 to memory space pointed to by ptr
Marshal.StructureToPtr(instance1, ptr, true);
Calculate the offset of data array field which should be at the end of an fvec_t2 struct
int offset = Marshal.SizeOf(typeof(fvec_t2));
get memory address of 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);
do the call
bool success = dllfunction(ptr);
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With