【问题标题】:A call to PInvoke function '[...]' has unbalanced the stack调用 PInvoke 函数 '[...]' 使堆栈不平衡
【发布时间】:2011-02-25 21:13:24
【问题描述】:

我在一些我已经使用了一段时间的东西上遇到了这个奇怪的错误。这可能是 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

标签: c# c++ pinvoke unmanaged managed


【解决方案1】:

我有一个 _cdecl c++ dll,我从 Visual Studio 2008 调用时没有遇到任何问题,然后 Visual Studio 2010 中的相同代码将无法工作。我得到了相同的 PInvoke ...也有不平衡堆栈错误。

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

[DllImport(CudaLibDir)] 

收件人:

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

我猜他们在 .NET 3.5 和 .NET 4.0 之间更改了 DLLImport 的默认调用约定?

【讨论】:

  • 我遇到了同样的问题,这个建议立即解决了它。谢谢斯科特! (微软——“自 1987 年以来为你的工作增加了不必要的复杂性!”)
  • 这也解决了我的问题!荣誉。 :)
  • 非常感谢!拯救了我的一天!
【解决方案2】:

也可能是在 .NET Framework 3.5 版中,默认情况下禁用了 pInvokeStackImbalance MDA。在 4.0(或者可能是 VS2010)下是enabled by default

是的。从技术上讲,代码总是错误的,而以前的版本 框架默默地纠正了它。

引用.NET Framework 4 Migration Issues document:“改进 与非托管代码的互操作性性能,不正确的调用 平台调用中的约定现在会导致应用程序失败。在 以前的版本,编组层解决了这些错误 堆栈...如果您有无法更新的二进制文件,您可以包括 应用程序配置文件中的 NetFx40_PInvokeStackResilience> 元素以启用调用 像早期版本一样在堆栈中解决错误。然而, 这可能会影响您的应用程序的性能。”

解决此问题的一种简单方法是指定调用约定并确保它与 DLL 中的相同。 __declspec(dllexport) 应该产生 cdecl 格式。

[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl)]

【讨论】:

    【解决方案3】:

    也许问题在于调用约定。你确定非托管函数被编译为 stdcall 而不是别的(我猜是 fastcall)?

    【讨论】:

    • 抱歉,我不太明白。我完全按照我在问题上所说的进行编译。我没有添加任何 __std 或类似的东西。在没有它之前它工作得很好。编辑:但现在显然在函数原型中添加 __std 并声明修复它。谢谢
    • 有这个确切的问题 - 我假设 declspec(dllexport) 出于某种原因使它们成为标准调用,但事实并非如此。更改我的 DLL 的调用约定以显式指定调用约定(然后也在 C# 中指定)解决了我的问题。
    【解决方案4】:

    使用以下代码,如果您的 DLL 具有名称 MyDLL.dll 并且您想在 Dll 中使用函数 MyFunction

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

    这对我有用。

    【讨论】:

    • 你在这里做了什么不同的事情?为什么它解决了这个问题?
    【解决方案5】:

    在我的情况下(VB 2010 和使用 Intel Fortran 2011 XE 编译的 DLL)当我的应用程序以 .NET Framework 4 为目标时存在问题。如果我将目标框架更改为 3.5 版,那么一切都按预期正常工作。 所以,我猜原因是 .Net Framework 4 中引入了一些东西,但我目前不知道是哪一个

    更新:问题已通过重新编译 Fortran DLL 并明确指定 STDCALL 作为 DLL 中导出名称的调用约定得到解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多