代码之家  ›  专栏  ›  技术社区  ›  santa

pinvokestackunbalance-从HookCallback调用非托管代码

  •  0
  • santa  · 技术社区  · 14 年前

    我的目标

    我想把左击转换成右击

    我的方法

    • 我通过setwindowshookex(user32.dll)注册一个低级钩子。
    • 过滤鼠标左键单击
    • 检查我是否要翻译特定的点击
    • 万一我真的想
      • 不要传递消息
      • 通过鼠标事件创建新的mouseclick(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);
      }
    

    调用鼠标事件失败,出现了pinvokestackunbalance异常,我想我应该关心这个异常。

    动态端口

    由于PinvokestackUnbalance通常是由于不正确的导入签名造成的,这里是我的:

      [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);
    

    出现

    在这种情况下,我隔离问题的正常方法失败了——因为鼠标事件调用本身可以工作,左键单击的破坏也可以工作。我讨厌当结构超过部分的总和时…

    3 回复  |  直到 14 年前
        1
  •  1
  •   Mattias S    14 年前

    鼠标事件签名应该是这样的

    public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, IntPtr dwExtraInfo);
    
        2
  •  2
  •   Daniel Rose    14 年前

    鼠标事件定义为:

    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表示uint ptr。所以正确地说应该是:

    [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注意到并非所有内容都已从堆栈中删除。它是为您做的(但是您传递了错误的数据,因此函数可能做了与您想要的不同的事情),并警告您。

        3
  •  2
  •   Hans Passant    14 年前

    有一个 许多 因特网上错误的P/Invoke声明。他们通常是在VB6中开始生活的。在这种语言中,整数是16位,长是32位。回到在16位操作系统上运行的vb1。它们在vb.net或c中不能正常工作。

    堆栈不平衡MDA专门设计用于捕获这样的错误声明,但它是一个选项(调试+异常)。如果只有一个参数或者传递了大量的零,那么实际的调用就会工作。不幸的是,代码在调用后保持正常运行的可能性也不坏。当方法返回时,堆栈指针值会自动更正。不过,你可能会遇到一些非常棘手的问题。