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

时间:2016-11-14 17:55:16

标签: c# c++ unity3d marshalling

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

我成功传递了一个带有数组字段的结构,一个带有标量字段的可变长度结构数组,但没有带有数组字段的可变长度结构数组。

这是我的C#代码。我省略了用C ++代码注册C#回调方法的代码,因为我不认为这是问题所在。除了特定的问题情况外,它的工作正常。

结构:

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

回调声明

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

对于有问题的情况,我的回调方法没有被调用(我没有得到日志输出)。我做了很多谷歌搜索,但没有发现任何涉及这种特殊情况的内容。我需要一个自定义封送器吗?

2 个答案:

答案 0 :(得分:1)

这不是一个答案,而是对你的回调调用的观察。自从我完成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);

答案 1 :(得分:0)

好的,在这里用另一种有效的方法回答我自己的问题。如果其他人出现并解决原先陈述的问题,我会不予理会。

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

[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 ++中分配的内存。