调用PInvoke函数'[...]'时未平衡堆栈

51

我在使用一些已经用了很长时间的内容时遇到了奇怪的错误。可能是Visual Studio 2010中的新功能,但我不确定。
我试图从C#中调用一个用C++编写的未托管函数。
根据我在互联网上阅读的和错误消息本身的内容,它与我的C#文件中的签名与C++中的签名不同有关,但我确实看不出来。
首先,以下是我的未托管函数:

TEngine GCreateEngine(int width,int height,int depth,int deviceType);

以下是我的 C# 函数:

[DllImport("Engine.dll", EntryPoint = "GCreateEngine", CallingConvention = CallingConvention.StdCall)]  
        public static extern IntPtr CreateEngine(int width,int height,int depth,int device);

当我调试C++时,我可以看到所有的参数都很好,所以我只能认为它与从TEngine(它是指向名为CEngine的类的指针)转换为IntPtr有关。我之前在VS2008中使用过这个没有问题。


大家好,我遇到了同样的问题,但是是在Visual Studio 2013中。我直接将C++ DLL的引用添加到我的C#项目中,在2010年中这个方法很好用,但是在2013年却不行了。我也已经提到了CallingConvention.Cdecl。 - Finisher001
5个回答

97

我有一个_cdecl c++ dll,在Visual Studio 2008中调用没有任何问题,但是在Visual Studio 2010中相同的代码无法运行。我得到了与PInvoke ... has unbalanced the stack错误相同的错误。

对我来说,解决方案是在DllImport(...)属性中指定调用约定:

从:

[DllImport(CudaLibDir)] 

收件人:

[DllImport(CudaLibDir, CallingConvention = CallingConvention.Cdecl)]

我猜.NET 3.5和.NET 4.0之间的DLLImport默认调用约定发生了变化?


50
在.NET Framework版本3.5中,pInvokeStackImbalance MDA默认已禁用。在4.0(或者可能是VS2010)中,默认启用了该功能。
是的,技术上说,代码一直存在问题,而之前的框架版本默默地纠正了它们。引用.NET Framework 4 Migration Issues document中的话:“为了提高与非托管代码的互操作性能,平台调用中的不正确调用约定现在会导致应用程序失败。在早期版本中,封送层将这些错误解决到堆栈上...如果您有无法更新的二进制文件,则可以在应用程序的配置文件中包含<NetFx40_PInvokeStackResilience>元素,以启用像早期版本中一样解决调用错误的功能。但是,这可能会影响应用程序的性能。”
修复此问题的简单方法是指定调用约定并确保其与DLL中的相同。__declspec(dllexport)应产生cdecl格式。
[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl)]

24

也许问题在于调用约定。你确定非托管函数是作为stdcall编译的,而不是其他什么(我猜测是fastcall)?


抱歉,但我真的不太明白。我按照我的问题所说的编译了代码。我没有添加任何__std或其他类似的东西。在这之前,它可以很好地运行。编辑:但现在显然在函数原型和声明中添加__std就可以修复它。谢谢。 - Sanctus2099
我曾经遇到过这个问题 - 我以为declspec(dllexport)会让它们成为stdcall,但事实并非如此。将我的DLL的调用约定更改为显式指定调用约定(然后在C#中也进行指定)解决了我的问题。 - ArtHare

5

如果你的DLL名称为MyDLL.dll,并且你想要在其中使用函数MyFunction,请使用以下代码:

[DllImport("MyDLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunction();

这对我很有帮助。


2
你在这里做了什么不同的事情?为什么它解决了问题? - Cody Gray

2
在我的情况下(使用VB 2010和使用Intel Fortran 2011 XE编译的DLL),当我的应用程序以.NET Framework 4为目标时,存在问题。如果我将目标框架更改为3.5版本,则一切都按预期正常工作。 因此,我猜测问题出在.NET Framework 4中引入的某些内容上,但我目前不知道具体是哪些。 更新:重新编译Fortran DLL并明确指定STDCALL作为导出名称的调用约定解决了该问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接