从C#调用C代码传递指针的正确性及异常解决问询
C#调用C代码传递指针引发内存访问异常的排查与解决
需求说明
需要从C#调用C代码,无需向C#暴露C的数据结构,仅传递指针即可。核心逻辑是C#决定操作流程,交由C执行后返回结果,C#无需了解C的具体实现细节。
初始实现代码
C代码
__declspec(dllexport) long cs_getB() { return &B; } __declspec(dllexport) int cs_order(long csa) { c_struct a = (c_struct *)csa; return order(a); } int order(c_struct a) { // 具体实现 return 0; }
C#代码
[DllImport("C:\\whatever.dll", EntryPoint = "cs_getB", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr GetB(); [DllImport("C:\\whatever.dll", EntryPoint = "cs_order", CallingConvention = CallingConvention.StdCall)] public static extern int Order(IntPtr a);
调用代码
IntPtr b = MyDll.GetB(); int bor = MyDll.Order(b);
遇到的问题
调用MyDll.Order时抛出异常:
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
更新信息
将C代码中的指针返回类型改为long long和intptr_t后,问题依旧。返回至C#的IntPtr值为0x00007ffef6de8750,且Visual Studio无法单步调试进入C代码。
更新后的相关C代码:
__declspec(dllexport) intptr_t getB() { return &B; }
其中B的定义:
struct thingy { long* m729; ... }; typedef struct thingy* thing; thing B;
问题分析与解决方案
1. 调用约定不匹配
C代码默认使用cdecl调用约定,但C#的DllImport指定了StdCall,这会导致栈平衡错误,引发内存访问异常。需统一调用约定:
- 方案一:C代码显式指定
__stdcall__declspec(dllexport) intptr_t __stdcall getB() { return (intptr_t)&B; } __declspec(dllexport) int __stdcall cs_order(intptr_t csa) { thing a = (thing)csa; return order(a); } - 方案二:C#的
DllImport改为CallingConvention.Cdecl[DllImport("C:\\whatever.dll", EntryPoint = "getB", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr GetB(); [DllImport("C:\\whatever.dll", EntryPoint = "cs_order", CallingConvention = CallingConvention.Cdecl)] public static extern int Order(IntPtr a);
2. 指针类型转换与参数不匹配
C代码中order函数的参数是c_struct a(值传递结构体),但实际传递的是结构体指针,存在类型不匹配:
- 如果
order需要结构体值,需在cs_order中解引用指针:int cs_order(intptr_t csa) { thing ptr = (thing)csa; return order(*ptr); // 解引用指针得到结构体值 } - 如果
order需要指针,直接修改order的参数类型:int order(thing a) { // 实现逻辑 return 0; }
3. 未初始化指针问题
检查B是否已正确初始化。thing B;仅声明了指针变量,未指向有效的结构体实例,访问其内存会触发错误。需确保B指向已分配的内存:
// 方式一:动态分配内存 thing B = (thing)malloc(sizeof(struct thingy)); // 方式二:指向全局结构体实例 struct thingy B_instance; thing B = &B_instance;
4. 调试问题
确保C项目编译为调试版本,在Visual Studio中启用启用本机代码调试(项目属性→调试→启用本机代码调试),才能单步进入C代码调试。
内容的提问来源于stack exchange,提问作者Richard Barraclough
相关产品推荐
相关产品推荐

