C#调用C++ DLL时触发PInvokeStackImbalance错误如何解决?
问题原因分析
PInvokeStackImbalance错误的核心诱因是托管与非托管代码的调用约定不匹配,同时还存在参数封送规则不一致的问题:
- C++函数定义使用了
WINAPI宏,Windows平台下该宏固定展开为__stdcall调用约定,由被调用方负责清理栈内存 - C#的
DllImport特性中指定了CallingConvention.Cdecl,该约定由调用方负责清理栈内存,二者栈清理逻辑冲突直接触发堆栈不平衡 - 此外C#中用
char[]传递字符串参数也存在封送错位:C#的char是2字节Unicode编码,C++的char是1字节ANSI编码,类型不匹配会额外增加栈空间计算错误的概率
修复方案
你可以任选以下任意一种统一调用约定的方案,同时调整参数封送规则即可:
方案1:修改C#侧配置适配C++现有定义
直接调整DllImport的调用约定和参数类型,不需要修改C++ DLL代码:
// 调用约定改为StdCall匹配WINAPI,char[]改为string匹配C++的char*字符串 [DllImport("Mydll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] public static extern bool MyFunction(string logPath, float f1, float f2);
调用时直接传入字符串类型的路径即可,不需要手动转字符数组。
方案2:修改C++侧定义适配C#现有配置
如果确认要使用Cdecl约定,去掉C++函数定义中的WINAPI宏即可:
// 去掉WINAPI宏默认使用Cdecl约定,形参char[]调整为更规范的const char* EXTERNC MYDLL_API bool MyFunction(const char* logPath, float f1, float f2);
额外注意事项
- 确认C#项目的目标平台和C++ DLL一致:C++编译的是Win32(x86)版本,C#项目属性的「生成」 tab 中需要把目标平台设置为
x86,不要使用Any CPU或x64 - 如果C++函数内部需要修改
logPath指向的内存内容,C#侧可以改用StringBuilder类型传递参数,提前分配足够的缓冲区大小即可
内容的提问来源于stack exchange,提问作者MrTrain
相关产品推荐
相关产品推荐

