我正在尝试使用 PostMessage 将密钥发送到窗口。但是在我的程序发送密钥后,调试窗口中出现错误。
出现的错误是:检测到 PInvoke 堆栈不平衡。
我想我调用 DLL 是错误的:
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, Int64 lParam);
方法签名在错误中。试试这个:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
您使用的是 32 位计算机,并且Int64
您的问题类型错误。见pinvoke.net
出现的错误是:检测到 PInvoke 堆栈不平衡。
您所看到的只是一点背景,Ria 的回答将解决您的问题。这实际上不是错误,而是MDA 产生的警告。托管调试助手,它是添加到 CLR、框架和调试器的额外代码,用于密切关注意外的运行时问题,并在使用调试器运行时激活。您可以查看可用的 MDA,使用 Debug + Exceptions 并展开“Managed Debugging Assistants”节点来查看它们。默认情况下,它们中的很多都被禁用,因为它们往往很吵,或者往往会产生错误的警告,或者以后已经产生了异常。请注意 pInvokeStackImbalance MDA 实际上并未在此处列出,它很特别,因为它无法关闭。
此处记录了 MDA 的完整列表。pInvokeStackImbalance 的描述包含对它检测到的问题原因的体面描述:
平台调用调用的托管签名可能与被调用方法的非托管签名不匹配。这种不匹配可能是由于托管签名没有声明正确数量的参数或没有为参数指定适当的大小。MDA 也可以激活,因为调用约定(可能由 DllImportAttribute 属性指定)与非托管调用约定不匹配。
我强调了您案例的真正原因,您的 lParam 参数类型错误。wParam 类型也是错误的,但碰巧有正确的大小。当您的代码在 64 位操作系统上运行时,这些事故往往会发生。
尽管这只是一个警告,并且当您忽略它并继续运行时,您的代码似乎运行良好,但堆栈不平衡是一种非常严重的运行时问题。故障模式是不可预测的,它可能在 Debug 版本中运行得很好,但在 Release 版本中会用一个难以理解的异常来轰炸你的程序。这里的具体问题是,当您调用该方法时,您在堆栈上推送了太多数据,而 Windows 没有从堆栈中弹出足够多的数据。当你经常调用它时,它会用这个站点的名称来轰炸你的程序。这个问题往往被隐藏起来,因为从 C# 方法返回会重新平衡堆栈。在发布模式下,抖动优化器内联该方法时,所有赌注都关闭。仅当您没有附加调试器时才会这样做,这是尝试诊断的最糟糕的错误。
找到正确的 pinvoke 声明并不是特别容易。声明它们的方法不止一种,您经常会发现关于声明的故意“谎言”,以使函数更容易被调用。pinvoke.net 网站是一个相当不错的来源,尽管它倾向于过度指定声明。就像在 Ria 的示例中一样,[return:] 属性是不必要的,因为这已经是 bool 的默认编组。当您不拥有窗口句柄时,HandleRef 没有多大意义。但它会工作得很好。另一个相当不错的来源是 Pinvoke Interop Assistant,您可以在此处找到该工具。它产生自动生成的声明,它们并不漂亮。
最后但同样重要的是,您不能使用 PostMessage 可靠地将击键插入另一个进程的窗口。当您需要进程的键盘状态准确时,您会遇到麻烦,尤其是 Shift、Alt 和 Ctrl 键。像 Alt+Gr 这样的死键出现在具有不同布局的键盘上,特别是对于有很多变音符号或大字母表的语言。要戳打字键,您应该改用 WM_CHAR。