Marshal包含可变长度数组的C结构

时间:2021-12-19 16:31:21

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.

我想将一个带有可变长度数组的C结构编组回C#,但到目前为止,我无法获得比指向结构表示和浮点指针更好的结果。

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.

我想要的是一个.NET样式数组,其中数据将是float [],但是如果我确实将结构更改为下面的表单,我会得到无法获取地址,获取大小或声明指针上面外部函数中的托管类型。

[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 ?

显然,不可能有一个可变长度的阵列按原样编组,这是正确的还是仍然有办法实现这一目标?

2 个解决方案

#1


6  

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

如果你不知道数组的长度这是你想要的,你可以将它转换为intprt并处理inptr但首先你需要创建2个结构

[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

为数据数组的扩展空间动态分配fvec_t2结构的空间

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

将fvec_t2的现有字段值传输到ptr指向的内存空间

Marshal.StructureToPtr(instance1, ptr, true);

Calculate the offset of data array field which should be at the end of an fvec_t2 struct

计算应该位于fvec_t2结构末尾的数据数组字段的偏移量

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

将数据复制到ptr

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

do the call

打电话

bool success = dllfunction(ptr);

Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;

#2


-1  

In the above case I'd definitely use the SizeParamIndex.

在上面的例子中,我肯定会使用SizeParamIndex。

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

Good luck.

#1


6  

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

如果你不知道数组的长度这是你想要的,你可以将它转换为intprt并处理inptr但首先你需要创建2个结构

[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

为数据数组的扩展空间动态分配fvec_t2结构的空间

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

将fvec_t2的现有字段值传输到ptr指向的内存空间

Marshal.StructureToPtr(instance1, ptr, true);

Calculate the offset of data array field which should be at the end of an fvec_t2 struct

计算应该位于fvec_t2结构末尾的数据数组字段的偏移量

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

将数据复制到ptr

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

do the call

打电话

bool success = dllfunction(ptr);

Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;

#2


-1  

In the above case I'd definitely use the SizeParamIndex.

在上面的例子中,我肯定会使用SizeParamIndex。

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

Good luck.