C#向C DLL传递含数组参数的回调时触发AccessViolationException如何解决
问题根因及修复方案
1. 调用约定不匹配
从给出的C头文件可以看到,当编译环境为WATCOMC时,导出函数的调用约定被指定为__stdcall,你当前C#代码中DllImport和UnmanagedFunctionPointer特性均指定为Cdecl,调用约定不匹配会直接导致栈不平衡,触发访问冲突。
优先尝试将两处调用约定修改为CallingConvention.StdCall,如果DLL是在非WATCOMC的Windows环境下编译的,再尝试切回Cdecl验证。
2. 数组参数封送逻辑错误
你需要在C#侧分配数组内存的场景下,C DLL传入回调的int*、double*为待赋值的空指针,你当前直接将委托参数声明为托管数组类型,CLR会尝试从空指针读取内存转成托管数组,必然触发访问错误。
正确实现逻辑
- 委托中的数组参数改为
IntPtr类型接收C侧传入的指针 - 在回调函数内部分配对应长度的托管数组,通过
Marshal类将托管数组内容拷贝到非托管指针指向的内存空间,或者直接分配非托管内存赋值给传入的指针 - 若为C DLL向数组写入数据返回给C#使用的场景,则反过来在回调内通过
Marshal.Copy将IntPtr指向的非托管内存拷贝到托管数组中即可
修复后代码示例
// 调用约定优先试StdCall,不生效再切换为Cdecl [DllImport(@"PathToDLL", EntryPoint = "CFunction", CallingConvention = CallingConvention.StdCall)] public static extern int CFunction(int callbackArraysLength, [MarshalAs(UnmanagedType.FunctionPtr)] SolutionCallbackFuncDelegate solutionCallbackFunc); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate int SolutionCallbackFuncDelegate(IntPtr intArrPtr, IntPtr doubleArrPtr, double d); public static int solutionFunction(IntPtr intArrPtr, IntPtr doubleArrPtr, double d) { int arraysLength = 5; // 场景1:C#侧分配内存,供C DLL写入数据 int[] intArr = new int[arraysLength]; double[] doubleArr = new double[arraysLength]; // 此处填充数组内容逻辑 // ... // 将托管数组拷贝到C侧传入的指针指向的内存 Marshal.Copy(intArr, 0, intArrPtr, arraysLength); Marshal.Copy(doubleArr, 0, doubleArrPtr, arraysLength); /* 场景2:C DLL填充数组,C#侧读取 int[] intArr = new int[arraysLength]; double[] doubleArr = new double[arraysLength]; Marshal.Copy(intArrPtr, intArr, 0, arraysLength); Marshal.Copy(doubleArrPtr, doubleArr, 0, arraysLength); // 后续处理数组内容逻辑 */ return 0; } static SolutionCallbackFuncDelegate solCBDel; public static void Main(string[] args) { solCBDel = new SolutionCallbackFuncDelegate(solutionFunction); int arraysLength = 5; int result = CFunction(arraysLength, solCBDel); }
内存分配位置说明
内存分配操作在回调函数solutionFunction内部实现即可,你预先定义的静态委托变量已经避免了GC回收委托对象的问题,无需额外处理。
内容的提问来源于stack exchange,提问作者Eran.G
相关产品推荐
相关产品推荐

