全局鼠标事件处理程序
Posted
技术标签:
【中文标题】全局鼠标事件处理程序【英文标题】:Global mouse event handler 【发布时间】:2012-07-21 08:53:06 【问题描述】:我有以下代码,我从某个地方获得来捕获鼠标事件。我修改了它并制作了一个事件处理程序,以便我可以订阅它。鼠标事件被正确捕获。但它永远不会触发事件处理程序。有人能弄清楚代码有什么问题吗?
public static class MouseHook
public static event EventHandler MouseAction = delegate ;
public static void Start() => _hookID = SetHook(_proc);
public static void stop() => UnhookWindowsHookEx(_hookID);
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(LowLevelMouseProc proc)
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
MouseAction(null,new EventArgs());
return CallNextHookEx(_hookID, nCode, wParam, lParam);
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
[StructLayout(LayoutKind.Sequential)]
private struct POINT
public int x;
public int y;
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
public POINT pt;
public uint mouseData, flags, time;
public IntPtr dwExtraInfo;
[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);
我是这样订阅的。
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(Event);
接收事件的函数。
private void Event(object sender, EventArgs e) => Console.WriteLine("Left mouse click!");
更新: 我将工作代码放在open source nuget package for user action hooks.
【问题讨论】:
这不能在控制台模式应用程序中工作,您的程序必须泵送消息循环。 Application.Run() 是必需的。 我实际上在我的 WPF 应用程序中使用了上面的代码。我从 App.cs 的 Onstartup 方法中调用 MouseHook 类。 对于所有感觉这会导致鼠标拖动的人,请在单独的提升进程中运行它并使用单独的线程来处理事件。 【参考方案1】:return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
当您在 Windows 8 之前的 Windows 版本上的 .NET 4 上运行此代码时,该代码将失败。CLR 不再模拟托管程序集的非托管模块句柄。您无法在代码中检测到此故障,因为它缺少所需的错误检查。在 GetModuleHandle 和 SetWindowsHookEx 上。 pinvoke 时不要跳过错误检查,winapi 不会抛出异常。检查它们是否返回 IntPtr.Zero 并在返回时简单地抛出 Win32Exception。
修复很简单,SetWindowsHookEx() 需要一个有效的模块句柄,但在设置低级鼠标挂钩时实际上并没有使用它。因此,任何句柄都可以,您可以传递 user32.dll 的句柄,该句柄始终加载在 .NET 应用程序中。修复:
IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
if (hook == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
return hook;
【讨论】:
谢谢。但我用修复修改了代码。当我在 HookCallback 函数中使用 Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y) 时,它曾经显示坐标。现在它不工作了。 呃,等等,这是从“它不工作”开始的。它实际上在工作吗?您至少可以记录您正在使用的 .NET 和 Windows 版本吗?您是否在“输出”窗口中看到第一次机会异常? 是的,我昨天工作时它正在给鼠标坐标。实际上,我检查了是否使用调试路径跟踪触发了事件。是的,它确实!但是 Console.WriteLine("something") 没有显示任何输出。我认为 console.writeline 工作不正常。奇怪 呃,等等,你现在是说事件实际上被触发了?听起来我的建议解决了你的问题。在您第一次检查 Debug.Print() 也不起作用之后,开始另一个关于 Console.WriteLine() 的问题。 是的!上面的建议很有用。但我发现 Visual Studio 设置不在 x86 调试模式下。我改变了它,现在它可以工作了!谢谢【参考方案2】:我刚刚将您的代码复制到一个简单的 Windows 窗体中,并且它的工作方式与您描述的一样。你是如何使用它的?您从哪里开始和附加活动?
为了完整起见,这是我最终得到的代码 - 从一个简单的 C# 表单模板开始
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
public partial class Form1 : Form
public Form1()
InitializeComponent();
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(Event);
private void Event(object sender, EventArgs e) Console.WriteLine("Left mouse click!");
public static class MouseHook
public static event EventHandler MouseAction = delegate ;
public static void Start()
_hookID = SetHook(_proc);
public static void stop()
UnhookWindowsHookEx(_hookID);
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(LowLevelMouseProc proc)
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
MouseAction(null, new EventArgs());
return CallNextHookEx(_hookID, nCode, wParam, lParam);
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
[StructLayout(LayoutKind.Sequential)]
private struct POINT
public int x;
public int y;
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
[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);
【讨论】:
如果我想知道mouseClicks的坐标怎么办? 适用于除第 5 个按钮之外的所有鼠标按钮事件。实际上第 4 个和第 5 个按钮返回相同的代码。 我刚做了这个。公共静态 int x = 0; // 进入 Program.cs 和 MyConsoleApp.Program.x = hookStruct.pt.x; // 在 HookCallback 中找到 它可以工作,但对我来说,它会导致我的应用在启动后的前 10 秒内显着变慢(即鼠标抖动)。 @Masum 您可以通过使用 lParam[10] 条目来区分第 4 个和第 5 个鼠标键(对于事件类型 523 [down] 和 524 [up])。对于第 4 个/前进键,条目 = 2;对于第 5 个/返回键,条目 = 1。【参考方案3】:对于任何未来的访客:
我已经实现了一个线程级鼠标钩子。
_process = Process.Start(@"c:\windows\notepad.exe");
//_process = Process.Start(@"c:\windows\syswow64\notepad.exe"); // works also with 32-bit
_mouseHook = new MouseHook(_process.Id);
_mouseHook.MessageReceived += MouseHook_MessageReceived;
_mouseHook.Install();
...
private void MouseHook_MessageReceived(object sender, MouseMessageEventArgs e)
Debug.WriteLine($"Mouse Message Code: e.MessageCode; X: e.X; Y: e.Y; Delta: e.Delta");
【讨论】:
以上是关于全局鼠标事件处理程序的主要内容,如果未能解决你的问题,请参考以下文章