我想要更新在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;
}
答案 0 :(得分:3)
让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 ++方法分配内存。
答案 1 :(得分:1)
如果您愿意允许C#分配数组(可能是更安全的替代方案),那么您可以使用标准PInvoke属性执行此行为。
将您的C ++声明更改为:
extern "C" __declspec(dllexport) bool PopulateArray(float resultVerts[], int resultVertLength)
和您的C#声明:
[DllImport("Win32Library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool PopulateArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] resultVerts, int resultVertLength);
您在C#端的使用将改为:
var resultVertices = new float[5];
PopulateArray(resultVertices, resultVertices.Length);