C++传递数组到C#时arrayPtr为null引发Marshal.Copy报错问题求解
问题原因
你的代码存在3个核心错误,直接导致指针返回null和后续潜在崩溃:
- C++侧函数参数是值传递的
int*,函数内修改arrayPtr只会改动函数栈上的局部副本,根本不会把地址回传给外层调用方,这是C#拿到空指针的直接原因 - C++函数内定义的
int array[] = {1,2}是栈上局部变量,函数返回时对应栈帧直接销毁,就算指针传递正确,拿到的也是指向已回收内存的野指针,读取时会出现脏数据或访问违例 - C#侧
Marshal.Copy的最后一个参数是待复制的元素个数,不是字节总长度,你传入计算出的字节数会导致内存拷贝越界
可行修复方案
方案1:C++侧分配堆内存回传(适合数组长度动态变化的场景)
需要修改C++侧参数为二级指针用于回传数组地址,配套内存释放接口避免泄漏,代码如下:
C++头文件定义
extern "C" GMSH_API void GMSH_Model_OCC_Fragments(int** outArrayPtr, int* outArrayLen); extern "C" GMSH_API void GMSH_Free_Array(int* arrayPtr);
C++实现代码
void GMSH_Model_OCC_Fragments(int** outArrayPtr, int* outArrayLen) { *outArrayLen = 2; // 必须在堆上分配数组内存,不能使用栈上局部变量 int* heapArray = new int[2]{1, 2}; *outArrayPtr = heapArray; } void GMSH_Free_Array(int* arrayPtr) { // 配套释放C++侧new出来的数组内存 delete[] arrayPtr; }
C#侧调用代码
[DllImport("GMSHCSHARP.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void GMSH_Model_OCC_Fragments(out IntPtr arrayPtr, out int arrayLen); [DllImport("GMSHCSHARP.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void GMSH_Free_Array(IntPtr arrayPtr); public void Create() { IntPtr arrayPtr = IntPtr.Zero; int arrayLen = 0; GMSH_Model_OCC_Fragments(out arrayPtr, out arrayLen); try { int[] returnArray = new int[arrayLen]; // 最后一个参数直接传元素个数,不需要计算字节长度 Marshal.Copy(arrayPtr, returnArray, 0, arrayLen); // 此处可正常使用returnArray } finally { if (arrayPtr != IntPtr.Zero) { GMSH_Free_Array(arrayPtr); } } }
方案2:C#侧预分配数组传入(适合数组长度固定的场景)
不需要来回传指针、手动拷贝内存,实现更简单,也没有内存泄漏风险:
C++实现代码
extern "C" GMSH_API void GMSH_Model_OCC_Fragments(int* outArray, int arrayLen); void GMSH_Model_OCC_Fragments(int* outArray, int arrayLen) { // 需保证传入的数组长度足够容纳要写入的数据 outArray[0] = 1; outArray[1] = 2; }
C#侧调用代码
[DllImport("GMSHCSHARP.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void GMSH_Model_OCC_Fragments(int[] outArray, int arrayLen); public void Create() { int[] returnArray = new int[2]; GMSH_Model_OCC_Fragments(returnArray, returnArray.Length); // 调用完成后returnArray直接拿到{1,2},可直接使用 }
注意事项
- 禁止返回C++函数内栈上局部变量的地址,函数退出后栈内存自动回收,指针会失效
- 要跨层回传指针时,C++侧必须使用二级指针接收参数,值传递的指针无法把地址写回外层
- 不要用
Marshal.FreeHGlobal释放C侧通过new分配的内存,二者内存堆不匹配,必须调用C侧配套的释放接口
内容的提问来源于stack exchange,提问作者Aris Theo
相关产品推荐
相关产品推荐

