我想更新在C#中创建的数组,然后将指向该数组的指针传递给C ++,然后让C ++填充索引,以供在C#中使用。现在,我正在使用Marshal.Copy()完成此任务,但我想避免可能不必要的复制,并返回c ++释放数组。这有可能吗?
这些数组是浮点数和整数,用于几何网格数据。
我当前的用法(正在工作,而不是我想使用的)C#
IntPtr intptr=new IntPtr(); int count = 0; PopulateArray(ref intptr, ref count); float[] resultVertices = new float[count]; Marshal.Copy(intptr, resultVertices, 0, count);
C ++
extern "C" __declspec(dllexport) bool PopulateArray(float** resultVerts, int* resultVertLength){ *resultVerts = new float[5]{0.123f, 3.141529f, 127.001f, 42.42f, 0}; int myX = 5; *resultVertLength = myX; return true; }
Erik.. 5
使C ++代码更新托管C#数组的唯一安全方法是固定该数组。否则,垃圾收集器有可能在本机代码运行时尝试移动数组。您可以使用GCHandle对象执行此操作。
int count = 5; float[] resultVertices = new float[count]; GCHandle handle = GCHandle.Alloc(resultVertices, GCHandleType.Pinned); IntPtr address = handle.AddrOfPinnedObject(); PopulateArray(address, count); handle.Free();
也可以使用不安全的代码来完成,这样阅读和记忆起来会更直观:
int count = 5; float[] resultVertices = new float[count]; unsafe { fixed(float* ptr = resultVertices) { PopulateArray(ptr, count); } }
另一种选择是让C#分配一个非托管内存块并将其传递给C ++方法。这比您所做的要好,因为您没有将分配/取消分配的责任放在C ++库代码中,而是将所有这些都保留在C#中。我知道您想避免遇到麻烦,但是有时复制比固定对象更有效,但这取决于它们的大小。我建议您进行性能测试以确定最适合您的情况。
int count = 5; float[] resultVertices = new float[count]; IntPtr unmanagedMemory = Marshal.AllocHGlobal(count * Marshal.SizeOf(typeof(float))); PopulateArray(unmanagedMemory, count); Marshal.Copy(unmanagedMemory, resultVertices, 0, count);
在所有这些情况下,您都应该将C ++代码设置为如下所示:
extern "C" __declspec(dllexport) bool PopulateArray(float* resultVerts, int vertLength) { resultVerts[0] = 0.123f; // fill out the rest of them any way you like. return true; }
如果数组大小是可变的,则建议使用单独的C ++方法来计算大小并返回大小,而不是让C ++方法分配内存。
使C ++代码更新托管C#数组的唯一安全方法是固定该数组。否则,垃圾收集器有可能在本机代码运行时尝试移动数组。您可以使用GCHandle对象执行此操作。
int count = 5; float[] resultVertices = new float[count]; GCHandle handle = GCHandle.Alloc(resultVertices, GCHandleType.Pinned); IntPtr address = handle.AddrOfPinnedObject(); PopulateArray(address, count); handle.Free();
也可以使用不安全的代码来完成,这样阅读和记忆起来会更直观:
int count = 5; float[] resultVertices = new float[count]; unsafe { fixed(float* ptr = resultVertices) { PopulateArray(ptr, count); } }
另一种选择是让C#分配一个非托管内存块并将其传递给C ++方法。这比您所做的要好,因为您没有将分配/取消分配的责任放在C ++库代码中,而是将所有这些都保留在C#中。我知道您想避免遇到麻烦,但是有时复制比固定对象更有效,但这取决于它们的大小。我建议您进行性能测试以确定最适合您的情况。
int count = 5; float[] resultVertices = new float[count]; IntPtr unmanagedMemory = Marshal.AllocHGlobal(count * Marshal.SizeOf(typeof(float))); PopulateArray(unmanagedMemory, count); Marshal.Copy(unmanagedMemory, resultVertices, 0, count);
在所有这些情况下,您都应该将C ++代码设置为如下所示:
extern "C" __declspec(dllexport) bool PopulateArray(float* resultVerts, int vertLength) { resultVerts[0] = 0.123f; // fill out the rest of them any way you like. return true; }
如果数组大小是可变的,则建议使用单独的C ++方法来计算大小并返回大小,而不是让C ++方法分配内存。