【问题标题】:PInvokeStackImbalance - invoking unmanaged code from HookCallbackPInvokeStackImbalance - 从 HookCallback 调用非托管代码
【发布时间】:2010-08-10 20:23:31
【问题描述】:

我的目标

我想把左键翻译成右键

我的方法

  • 我通过 SetWindowsHookEx (user32.dll) 注册了一个低级挂钩
  • 过滤鼠标左键点击
  • 检查我是否要翻译特定的点击
  • 如果我真的想
    • 不要传递消息
    • 通过 mouse_event 创建一个新的鼠标点击(user32.dll 也是)

问题

当我这样做时:

  private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
     if(nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam && doRight) {
        doRight = false;
        MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
        mouse_event(/*right down + right up*/8 | 16, hookStruct.pt.x, hookStruct.pt.y, 0, 0);
        return new IntPtr(1);
     }
     return CallNextHookEx(_hookID, nCode, wParam, lParam);
  }

对 mouse_event 的调用失败并出现 PInvokeStackImbalance-Exception,我想,我应该关心。

DllImports

因为通常 PInvokeStackImbalance 是由于不正确的导入签名而出现的,所以这里是我的:

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  private static extern bool UnhookWindowsHookEx(IntPtr hhk);

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

  [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr GetModuleHandle(string lpModuleName);

  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

出现

在这种情况下,我隔离问题的常规方法会失败 - 因为 mouse_event-call 本身有效,并且左键单击的垃圾也有效。当结构超过部分的总和时,我讨厌它......

【问题讨论】:

    标签: c# .net pinvoke mouse-hook


    【解决方案1】:

    mouse_event 定义为:

    VOID WINAPI mouse_event(
      __in  DWORD dwFlags,
      __in  DWORD dx,
      __in  DWORD dy,
      __in  DWORD dwData,
      __in  ULONG_PTR dwExtraInfo
    );
    

    WINAPI 表示 StdCall,DWORD 是 uint,ULONG_PTR 是 UIntPtr。所以正确的应该是:

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo);
    

    问题是 long 在 C# 中定义为 64 位,而 (u)int 是 32 位。 (U)IntPtr 是 32 位还是 64 位,取决于操作系统的位数。

    编辑:换句话说,您向函数传递了太多数据。由于被调用者清理了堆栈,因此 PInvoke 注意到并非所有内容都从堆栈中删除。它会为您执行此操作(但您传递了错误的数据,因此该函数可能执行了与您想要的不同的操作)并警告您。

    【讨论】:

    • 我知道 CLR 数据类型定义,我只是愚蠢到期望我们的生产代码是正确的 :)
    【解决方案2】:

    互联网上有很多 很多 错误的 p/invoke 声明。他们通常从 VB6 开始。在那种语言中,Integer 是 16 位,Long 是 32 位。回到在 16 位操作系统上运行的 VB1。它们在 VB.NET 或 C# 中无法正常工作。

    堆栈不平衡 MDA 是专门为捕获此类错误声明而设计的,但它是一个选项(调试 + 异常)。如果只有一个参数或传递大量零,则实际调用往往会起作用。不幸的是,代码在调用后继续正常运行的几率也不错。当方法返回时,堆栈指针值会自行更正。不过,您可能会遇到一些非常棘手的问题。

    【讨论】:

      【解决方案3】:

      mouse_event 签名应该是这样的

      public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, IntPtr dwExtraInfo);
      

      【讨论】:

      • 该死的我是不是傻 - 我什至说通常签名是错误的......但现在我想知道 - 我从另一个项目(生产代码)复制了签名,它在那里工作......发生了什么?
      • P/Invoke 对不正确的签名有点宽容,尽管在您的情况下,不正确的数据可能会传递给函数。为了警告您签名不正确,PInvokeStackImbalance 托管调试助手会在它处于活动状态时触发。
      猜你喜欢
      • 2013-08-24
      • 2011-01-24
      • 2015-02-24
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 2013-12-29
      • 1970-01-01
      • 2011-02-05
      相关资源
      最近更新 更多