无法在Unity中编组从C ++到C#的结构数组

我正在尝试将C ++中的struct数组传递给C#中的Unity脚本。 当我在生产中使用代码时,数组的大小会有很大差异,所以我实际上需要传递一个未知长度的数组。

我的聪明想法是将数组存储在堆上,并将对该数组的引用传递给Unity。 我发现StackOverflow的post就是如何做到的。 但是然后Unity抱怨引用为null。

这是我的C ++代码的一部分:

 extern "C" struct S; // Not currently used. struct S { int i; float f; }; extern "C" bool helloWorld(S ** a, int * i); S * s; bool helloWorld(S ** a, int * i) { s = new S[4]; for (int j = 0; j < 4; j++) { s[j].i = j; // Fill in placeholder s[j].f = (float) j; // data for now. } *a = s; // I don't know if this works. *i = 4; // Works. return true; } 

我尝试使用int而不是struct s并且它有效。

现在,我的C#代码是:

 [StructLayout(LayoutKind.Sequential),Serializable] public struct S { int i; float f; }; void Start() { IntPtr ptrNativeData = IntPtr.Zero; int itemsLength = 0; bool success = helloWorld(ref ptrNativeData, ref itemsLength); if (!success) { return; } S[] SArray = new S[itemsLength]; // Where the final data will be stored. IntPtr[] SPointers = new IntPtr[itemsLength]; Debug.Log("Length: " + itemsLength); // Works! Marshal.Copy(ptrNativeData, SPointers, 0, itemsLength); // Seems not to work. for (int i = 0; i < itemsLength; i++) { Debug.Log("Pointer: " + SPointers[i]); // SPointers[0] prints 0. Marshal.PtrToStructure(SPointers[i], SArray[i]); // Crashes here. Boom. } } [DllImport("TestUnity")] private static extern bool helloWorld(ref IntPtr ptrResultVerts, ref int resultVertLength); 

Marshal.PtrToStructure指令表示SPointers [i]参数为null。 我检查了Debug.Log命令,它确实看起来像是null:它打印为0。

但我尝试了类似于早期的int数组的东西,并且有效。 我不确定的是:我在C ++或C#中的问题是什么? 我没有传递正确的信息,还是我以错误的方式处理正确的信息?

解决方案1

这是我第一个独自提出的解决方案。 第二种解决方案更好。

完全要感谢Alex Skalozub,我想通了。 在编组期间,一级指针间接被抽象出来。 所以现在,我的C ++代码包含:

 S ** s; // Not a pointer to Ss, but a pointer to pointers to Ss. bool helloWorld(S *** a, int * i) { // Pass a triple pointer. s = new S * [4]; // Create array of pointers. for (int j = 0; j i = j; s[j]->f = (float) j; } *a = s; *i = 4; return true; } 

我的C#代码包含:

 for (int i = 0; i < itemsLength; i++) { Debug.Log("Pointer: " + SPointers[i]); SArray[i] = (S) Marshal.PtrToStructure(SPointers[i], typeof(S)); } 

就是这样! 所有数据都被传输。

现在这确实给我带来了内存管理问题: 必须释放在C ++代码中创建的每个对象 。 我知道这一点,接下来我会照顾它。

解决方案2

请参阅选中的答案。 亚历克斯的答案要好得多,因为它更少使用不必要的指针。 我使用了更多指针,因为我只是想出了如何在C#中使用它们。

您的ptrNativeData是指向结构数组本身的指针,而不是指向结构的指针数组。 将它复制到指针数组并访问它们是不正确的。

我还建议在声明互操作函数时使用而不是ref 。 这更准确,并且不需要编组器将初始值从托管代码复制到本机代码(因为它们在本机代码中初始化):

 [DllImport("TestUnity")] private static extern bool helloWorld(out IntPtr ptrResultVerts, out int resultVertLength); void Start() { IntPtr ptrNativeData; int itemsLength; bool success = helloWorld(out ptrNativeData, out itemsLength); if (!success) { return; } S[] SArray = new S[itemsLength]; // Where the final data will be stored. Debug.Log("Length: " + itemsLength); IntPtr p = ptrNativeData; for (int i = 0; i < itemsLength; i++) { Marshal.PtrToStructure(p, SArray[i]); p += Marshal.SizeOf(typeof(S)); // move to next structure } // todo: free ptrNativeData later }