PInvokeStackImbalance - 从HookCallback调用非托管代码

时间:2010-08-10 11:08:37

标签: c# .net pinvoke mouse-hook

我的目标

我想将左键单击翻译为右键单击

我的方法

  • 我通过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本身可以正常工作,而左键单击的垃圾也是如此。当结构超过各部分的总和时,我讨厌它......

3 个答案:

答案 0 :(得分:2)

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注意到并非所有内容都从堆栈中删除。它为你做了(但你传递了错误的数据,所以函数可能做了一些不同于你想要的东西)并警告你。

答案 1 :(得分:2)

Internet上存在错误的p / invoke声明 lot 。它们通常在VB6中开始。在该语言中,Integer为16位,Long为32位。回到在16位操作系统上运行的VB1。它们在VB.NET或C#中无法正常工作。

堆栈不平衡MDA专门用于捕获此类错误声明,但它是一个选项(Debug + Exceptions)。如果只有一个参数或传递大量零,则实际调用会起作用。不幸的是,代码在通话后保持正常运行的可能性也不错。当方法返回时,堆栈指针值会自行更正。你可以得到一些非常棘手的问题。

答案 2 :(得分:1)

mouse_event签名应该是这样的

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