不能从名帅C ++ stucts数组到C#在Unity [英] Can't marshal array of stucts from C++ to C# in Unity

查看:174
本文介绍了不能从名帅C ++ stucts数组到C#在Unity的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从C ++的传球结构的数组在C#一个统一的脚本。当我在生产中使用的代码中,数组的大小会相差很大,所以能有效地需要通过未知长度的数组。



我的聪明的想法是,存储在堆上数组,并传递给数组统一的参考。我发现了如何做到这一点的StackOverflow职位。但随后统一抱怨参考被空



下面是我的C ++的一部分代码:



<预类=郎-c ++ prettyprint-覆盖> 的externC结构S; //当前未使用。

的struct {
INT I;
浮动F;
};

的externC布尔的helloWorld(S ** A,INT * I);

S * S;

布尔的helloWorld(S **一,为int * I){
S =新的S [4];
为(INT J = 0; J< 4; J ++){
S [J]。我= j的; //填写占位符
S [J] .F =(浮点)焦耳; //现在的数据。
}
* A = S; //我不知道,如果这个工程。
* I = 4; //作品。
返回真;
}



我试图与 INT !而非结构和它的工作。



现在,我的C#代码是:

  [StructLayout(LayoutKind.Sequential),序列化] 
公共的struct {
INT I;
浮动F;
};

无效的start(){
IntPtr的ptrNativeData = IntPtr.Zero;
INT itemsLength = 0;

BOOL成功=的helloWorld(REF ptrNativeData,裁判itemsLength);
如果(成功!){
的回报;
}

S [] = SArray新S [itemsLength] //如果最后的数据将被存储。
IntPtr的[] SPointers =新的IntPtr [itemsLength]

的debug.log(长度+ itemsLength); //作品!
Marshal.Copy(ptrNativeData,SPointers,0,itemsLength); //似乎不起作用。

的for(int i = 0; I< itemsLength;我++){
的debug.log(指针+ SPointers [I]); // SPointers [0]将打印0
Marshal.PtrToStructure(SPointers [I],SArray [I]); //这里崩溃。繁荣。
}
}

函数[DllImport(TestUnity)]
私人静态外部布尔的helloWorld(REF的IntPtr ptrResultVerts,
REF INT resultVertLength);



Marshal.PtrToStructure 指令说, SPointers [I]参数为null。我检查的命令的debug.log,事实上它似乎空:它打印为0



不过,我试着用的数组类似的东西INT 更加早期和工作。什么我不知道的是:是我在C ++或C#的问题?我不能传递正确的信息还是我处理的方式是错误的正确的信息?



解决方法1



这是我在我自己的大部分想出了第一个解决方案。第二个解决方案是更好的。



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

  s ^ ** S; //不是一个指针SS,而是一个指向指针SS。 

布尔的helloWorld(S ***一,为int * I){//传递一个三重指针。
S =新S * [4]; //创建指针数组。
为(INT J = 0; J< 4; J ++){
S [J] =新S; //实际创建的每个对象。
S [J] - I标记= j的;
S [J] - > F =(浮点)焦耳;
}
* A = S;
* I = 4;
返回真;
}

和我的C#代码中包含:

 的for(int i = 0; I< itemsLength;我++){
的debug.log(指针+ SPointers [I]);
SArray [I] =(S) - Marshal.PtrToStructure(SPointers [I]的typeof(S));
}



这就是它!所有的数据传输



现在这并留下我一个内存管理问题:在C ++代码中创建的每一个对象都被释放。我所知道的是,和我要照顾它旁边。



解决方案2



查看核对答案。 Alex的答案是要好得多,因为它使更少使用不必要的指针。我用更多的指针,因为我只是想出了如何在C#中使用它们。


解决方案

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



我也建议使用退出而不是 REF 宣布的互操作功能时。这是更准确,从托管到本机代码不需要编组复制初始值(因为他们在本机代码正在初始化):

 函数[DllImport(TestUnity)] 
私人静态外部布尔的helloWorld(出的IntPtr ptrResultVerts,OUT INT resultVertLength);

无效的start(){
IntPtr的ptrNativeData;
INT itemsLength;

BOOL成功=的helloWorld(出ptrNativeData,出itemsLength);
如果(成功!){
的回报;
}

S [] = SArray新S [itemsLength] //如果最后的数据将被存储。

的debug.log(长度+ itemsLength);

IntPtr的P = ptrNativeData;
的for(int i = 0; I< itemsLength;我++){
Marshal.PtrToStructure(P,SArray [I]);
P + = Marshal.SizeOf(typeof运算(S)); //移动到下一个结构
}

// TODO:免费ptrNativeData后
}


I'm trying to pass an array of structs from C++ to a Unity script in C#. When I am using the code in production, the size of the array will vary greatly, so I effectively need to pass an array of unknown length.

My clever idea was to store the array on the heap and pass a reference to that array to Unity. I've found StackOverflow posts on how to do that. But then Unity complains about a reference being null.

Here's part of my C++ code:

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

I tried this with ints instead of structs and it worked.

Now, my C# code is:

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

The Marshal.PtrToStructure instruction says that the SPointers[i] argument is null. I checked with the Debug.Log command and indeed it does seem null: it prints as 0.

But I tried something similar with an array of ints earlier and that worked. What I'm not sure of is: is my problem in the C++ or in the C#? Am I not passing the right information or am I processing the right information in the wrong way?

Solution 1

This was the first solution I came up with mostly on my own. The second solution is better.

Totally thanks to Alex Skalozub, I figured it out. One level of pointer indirection gets abstracted out during marshalling. So now, my C++ code contains:

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 < 4; j++) {
    s[j] = new S; // Actually create each object.
    s[j]->i = j;
    s[j]->f = (float) j;
  }
  *a = s;
  *i = 4;
  return true;
}

And my C# code contains:

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

And that's it! All the data is transferred.

Now this does leave me with a memory management problem: every single object created in the C++ code will have to be freed. I'm aware of that, and I'm going to take care of it next.

Solution 2

See the checked answer. Alex's answer is much better as it makes much less use of unnecessary pointers. I used more pointers because I'd just figured out how to use them in C#.

解决方案

Your ptrNativeData is a pointer to array of structures themselves, not to array of pointers to structures. Copying it to array of pointers and accessing them is incorrect.

I'd also suggest to use out instead of ref when declaring an interop function. This is more accurate and doesn't require marshaller to copy initial values from managed to native code (as they're initialized in native code):

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

这篇关于不能从名帅C ++ stucts数组到C#在Unity的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆