如何修复"检测到Pinvoke堆栈不平衡"错误

hhh*_*hhh 2 c# pinvoke

我正在尝试使用PostMessage将密钥发送到窗口.但是在我的程序发送密钥后,我在调试窗口中收到错误.

出现的错误是:检测到PInvoke堆栈不平衡.

我想我错误地调用了DLL:

[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, Int64 lParam);
Run Code Online (Sandbox Code Playgroud)

Ria*_*Ria 5

方法签名是错误的.试试这个:

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
Run Code Online (Sandbox Code Playgroud)

您使用的是32位计算机,Int64但问题类型错误.见pinvoke.net


Han*_*ant 5

出现的错误是:检测到PInvoke堆栈不平衡.

只要你看到背后的一些背景,Ria的答案将解决你的问题.这实际上不是一个错误,它是由MDA产生的警告.托管调试助手,它是添加到CLR,框架和调试器的额外代码,用于监视意外的运行时问题,并在使用调试器运行时激活.您可以查看可用的MDA,使用Debug + Exceptions并展开"Managed Debugging Assistants"节点以查看它们.默认情况下,它们中的很多都被禁用,因为它们往往会产生噪音或倾向于生成错误警告或稍后已经生成异常.请注意,pInvokeStackImbalance MDA实际上并未列在那里,它是特殊的,因为它无法关闭.

此处记录完整的MDA列表.pInvokeStackImbalance的描述包含对其检测到的问题原因的正确描述:

平台调用调用的托管签名可能与正在调用的方法的非托管签名不匹配.这种不匹配可能是由于托管签名未声明正确的参数数量或未指定参数的适当大小.MDA也可以激活,因为可能由DllImportAttribute属性指定的调用约定与非托管调用约定不匹配.

我强调了你的情况下的真正原因,你的lParam参数有错误的类型.wParam类型也是错误的,但碰巧具有正确的大小.当您的代码在64位操作系统上运行时,这类事故往往会出现字节.

虽然这只是一个警告,并且当您忽略它并继续运行时,您的代码似乎运行正常,但堆栈不平衡是一种非常严重的运行时问题.失败模式是不可预测的,它可能在Debug构建中运行得很好但是在Release版本中用一个不可思议的异常轰炸你的程序.这里的具体问题是,当您调用该方法时,您在堆栈上推送了太多数据,并且Windows没有从堆栈中弹出足够的数据.当你经常打电话时,哪个会用这个网站的名称轰炸你的程序.该问题往往被隐藏,因为从C#方法返回会重新平衡堆栈.在释放模式下,所有投注均关闭,抖动优化器会内联该方法.只有当你没有连接调试器时才会这样做,这是尝试诊断的最糟糕的bug.

找到合适的pinvoke声明并不是特别容易.声明它们的方法不止一种,你经常会发现有关声明的故意"谎言",使函数更容易被解释.pinvoke.net网站是一个相当不错的来源,尽管它倾向于过度指定声明.就像在Ria的例子中一样,[return:]属性是不必要的,因为它已经是bool的默认封送.当你没有窗口句柄时,HandleRef没有多大意义.但它会工作得很好.另一个相当不错的来源是Pinvoke Interop助手,你可以在这里找到一个工具.它产生自动生成的声明,它们并不漂亮.

最后但并非最不重要的是,您不能使用PostMessage可靠地将击键戳到另一个进程的窗口中.当您需要过程的键盘状态准确时,尤其是Shift,Alt和Ctrl键时,您将遇到麻烦.像Alt + Gr这样的死键存在于具有不同布局的键盘上,特别是对于具有大量变音符号或大字母的语言.要戳打字键,你应该更喜欢WM_CHAR.