PInvokeStackImbalance C# 调用非托管 C++ 函数

Posted

技术标签:

【中文标题】PInvokeStackImbalance C# 调用非托管 C++ 函数【英文标题】:PInvokeStackImbalance C# call to unmanaged C++ function 【发布时间】:2011-01-24 07:51:24 【问题描述】:

切换到 VS2010 后,托管调试助手显示一个错误,即从 C# 应用程序调用非托管 C++ 函数导致堆栈不平衡。

通常的嫌疑人似乎没有导致问题。还有什么我应该检查的吗? VS2008 构建的 C++ dll 和 C# 应用程序从来没有问题,没有奇怪或神秘的错误 - 是的,我知道这并不意味着什么。

检查的内容如下:

dll 名称正确。 入口点名称是正确的,并且已经使用depends.exe 进行了验证 - 代码必须使用经过修改的名称,并且确实如此。 调用约定是正确的。 大小和类型似乎都正确。 字符集正确。 忽略错误后似乎没有任何问题,在调试器之外运行时也没有问题。

C#:

[DllImport("Correct.dll", EntryPoint = "SuperSpecialOpenFileFunc", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern short SuperSpecialOpenFileFunc(ref SuperSpecialStruct stuff);

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct SuperSpecialStruct

   public int field1;
   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
   public string field2;
   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
   public string field3;
   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
   public string field4;
   public ushort field5;
   public ushort field6;
   public ushort field7;
   public short field8;
   public short field9;
   public uint field10;
   public short field11;
;

C++:

short SuperSpecialOpenFileFunc(SuperSpecialStruct * stuff);

struct SuperSpecialStruct

   int               field1;
   char              field2[256];
   char              field3[20];
   char              field4[10];
   unsigned short    field5;
   unsigned short    field6;
   unsigned short    field7;
   short             field8;
   short             field9;
   unsigned int      field10;
   short             field11;
;

这是错误:

托管调试助手 'PInvokeStackImbalance' 检测到一个 “托管应用程序路径”中的问题。

附加信息:致电 PInvoke 函数 'SuperSpecialOpenFileFunc' 有 堆栈不平衡。这很可能 因为托管的 PInvoke 签名 与非托管目标不匹配 签名。检查调用 公约和参数 PInvoke 签名匹配目标 非托管签名。

【问题讨论】:

是的,我也遇到了这个问题,但不知道为什么。你会认为如果调用约定被搞砸了,它只会完全破坏堆栈并且它不会工作。它为我打的每一个电话都声明了这一点。 同样的问题,不知道为什么。不过,程序仍然有效。 哦,对我来说,将 c++ 函数更改为 __stdcall 解决了问题。 从 .NET Framework 4 开始,流线型的互操作封送架构为从托管代码到非托管代码的转换提供了显着的性能改进。在早期版本的 .NET Framework 中,封送层在 32 位平台上检测到不正确的平台调用声明并自动修复堆栈。 this url has explaned this 【参考方案1】:

如 Dane Rose's comment 中所述,您可以在 C++ 函数上使用 __stdcall 或在 DllImport 上声明 CallingConvention = CallingConvention.Cdecl

【讨论】:

【参考方案2】:

您在 C# 中指定 stdcall 但在 C++ 中未指定,此处的不匹配将导致函数和调用者都从堆栈中弹出参数。

另一方面,有一个编译器开关会打开 stdcall 作为默认调用约定,(-Gz) 你在使用它吗?

或者在你的 C++ 中试试这个

short __stdcall SuperSpecialOpenFileFunc(SuperSpecialStruct * stuff);

【讨论】:

【参考方案3】:

您在结构的 C# 声明中没有指定填充,但在 C++ 版本中没有。由于您混合的 char 数组并非都是 4 的倍数和奇数个 2 字节短,因此编译器可能会在结构中插入填充并添加结尾。

尝试将结构包装在 #pragma pack 中以确保没有填充。

#pragma pack(push)
#pragma pack(1)

// The struct

#pragma pack(pop)

【讨论】:

是的,无论如何都应该放入#pragma 包,但我认为也不是。您看不到它,但 C# 应用程序将使用 Marshal.Sizeof 计算的结构的大小传递给 C++ dll,C++ dll 验证由 sizeof() 计算的 C++ 结构大小与值相同进来了。【参考方案4】:

遇到了与描述相同的问题 - 多年来一直运行良好的非托管 C++ 应用程序。当我们升级到 VS2010 时,我们开始收到 PInvokeStackUnbalanced 消息。

如上所述将“__stdcall”添加到 C++ 签名中,问题就消失了。

【讨论】:

【参考方案5】:

很好。我更新函数定义如下:

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

效果很好。

【讨论】:

以上是关于PInvokeStackImbalance C# 调用非托管 C++ 函数的主要内容,如果未能解决你的问题,请参考以下文章

pInvokeStackImbalance 调试异常的解决办法。

PinvokeStackImbalance 发生。抛出此异常,但我不知道错误是啥?

对于这个简单的示例,为啥我会得到“检测到 PInvokeStackImbalance”?

StringBuilder 编组导致 PInvokeStackImbalance 异常

在 VB.NET 2010 中使用 GetAsyncKeyState 时的 PInvokeStackImbalance

从c#访问c++ dll库[重复]