从c++到c#的包含数组字段的结构体的封送数组

本文关键字:数组 字段 结构体 包含 c++ | 更新日期: 2023-09-27 18:17:47

我想在我的c#代码中定义一个回调函数,并将其传递给一些本地c++代码,然后让c++代码稍后调用它。这个回调函数需要接收一个可变长度的结构体数组,每个结构体包含一些数组字段。

我已经成功地传递了一个带有数组字段的结构体和一个带有标量字段的变长结构体数组,但没有传递一个带有数组字段的变长结构体数组。

这是我的c#代码。我省略了用c++代码注册c#回调方法的代码,因为我认为这不是问题所在;除了特定的有问题的情况,它工作得很好。

结构:

[StructLayout(LayoutKind.Sequential)]
public struct Foo
{
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.R4, SizeConst = 2)]
    public float[] a;
}

callback声明

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void Callback(Int32 count, 
                          [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Foo[] foos);

回调方法本身

public void onFoo(Int32 count, Foo[] foos) {
        Debug.Log("in onFoo callback, foo values=" + foo[0].a + ", " + foo[1].a);
}
下面是c++代码:

首先是结构体:

typedef struct {
    float a[2];
} Foo;

和回调调用:

Foo* foos = new Foo[2];
foos[0].a[0] = 1.11;
foos[0].a[1] = 2.22;
foos[1].a[0] = 3.33;
foos[1].a[1] = 4.44;
onFoo(2, foos);
delete[] foos;

对于有问题的情况,没有调用我的回调方法(我没有得到日志输出)。我在谷歌上搜索了很多,但没有找到任何涉及这种特殊情况的内容。我需要自定义编组器吗?

从c++到c#的包含数组字段的结构体的封送数组

这不是答案,而是对回调调用的观察。我做c++已经很多年了,但是下面的

Foo* foos = new Foo[2];
foos[0].a = 1.11;
foos[1].a = 2.22;
onFoo(2, foos);

Foo* foos = new Foo[2];
foos[0].a[0] = 1.11;
foos[0].a[1] = 1.12;
foos[1].a[0] = 2.22;
foos[1].a[1] = 2.23;
onFoo(2, foos);

好了,用另一种可行的方法来回答我自己的问题。我将保留它不被接受,以防其他人出现并解决最初陈述的问题。

的解决方法是传递一个结构指针数组给回调函数,而不是传递一个结构数组。回调签名更改为

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void Callback(Int32 count, 
                       [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] IntPtr[] fooPtrs);

,在c++代码中,我分配了Foo*的数组,然后分别分配和初始化每个Foo,并将其指针存储到数组中。

Foo** foos = new Foo*[2];
Foo* foo1 = new Foo();
foo1->a[0] = 1.11;
foo1->a[1] = 2.22;
foos[0] = foo1;
Foo* foo2 = new Foo();
foo2->a[0] = 3.33;
foo2->a[1] = 4.44;
foos[1] = foo2;
onFoo(2, foos);
delete foo1;
delete foo2;
delete[] foos;

回到c#端,在回调方法中,我分配了一个Foo数组,循环传入的IntPtr数组,并使用marshal封送每个元素。PtrToStructure,如下:

public void onFoo(Int32 count, IntPtr[] FooPtrs)
{
    Foo[] foos = new Foo[count];
    for (int i=0; i< count; i++)
    {
        foos[i] = (Foo)Marshal.PtrToStructure(ptrs[i], typeof(Foo));
    }
}
这种方法的缺点是涉及到更多的内存分配和复制,而不是能够将c# struct数组直接映射到c++中分配的内存。