0

我的目标

我想将左键单击转换为右键单击

我的方法

  • 我通过 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 本身有效,并且左键单击的垃圾也有效。当结构超过部分的总和时,我讨厌它......

4

3 回答 3

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

于 2010-08-10T11:22:02.710 回答
2

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

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

于 2010-08-10T12:23:31.123 回答
1

mouse_event 签名应该是这样的

public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, IntPtr dwExtraInfo);
于 2010-08-10T11:14:05.703 回答