c# 全局监听鼠标(鼠标钩子) 作者:马育民 • 2024-08-16 15:19 • 阅读:10024 # 封装类 ``` internal class MouseHook { #region 常量 public const int WM_MOUSEMOVE = 0x200; // 鼠标移动 public const int WM_LBUTTONDOWN = 0x201;// 鼠标左键按下 public const int WM_RBUTTONDOWN = 0x204;// 鼠标右键按下 public const int WM_MBUTTONDOWN = 0x207;// 鼠标中键按下 public const int WM_LBUTTONUP = 0x202;// 鼠标左键抬起 public const int WM_RBUTTONUP = 0x205;// 鼠标右键抬起 public const int WM_MBUTTONUP = 0x208;// 鼠标中键抬起 public const int WM_LBUTTONDBLCLK = 0x203;// 鼠标左键双击 public const int WM_RBUTTONDBLCLK = 0x206;// 鼠标右键双击 public const int WM_MBUTTONDBLCLK = 0x209;// 鼠标中键双击 public const int WH_MOUSE_LL = 14; //可以截获整个系统所有模块的鼠标事件。 public const int WM_WHEELDOWN = 519; // 按下鼠标滑轮 public const int WM_WHEELUP = 520; // 松开鼠标滑轮 public const int WM_WHEELSCROLL = 522; // 滚动滑轮 public const int WM_SCROLLBACK = -0x780000;// 向后滚动滑轮 public const int WM_SCROLLFORWARD = 0x780000;// 向前滚动滑轮 #endregion #region 成员变量、回调函数、事件 /// /// 钩子回调函数 /// /// 如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册) /// 记录了按下的按钮 /// /// public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); /// /// 全局的鼠标事件 /// /// 代表发生的鼠标的事件 /// 钩子的结构体,存储着鼠标的位置及其他信息 public delegate void MyMouseEventHandler(Int32 wParam, MouseHookStruct mouseMsg); private event MyMouseEventHandler OnMouseActivity; // 声明鼠标钩子事件类型 private HookProc _mouseHookProcedure; private static int _hMouseHook = 0; // 鼠标钩子句柄 // 锁 private readonly object lockObject = new object(); // 当前状态,是否已经启动 private bool isStart = false; #endregion #region Win32的API /// /// 钩子结构体 /// [StructLayout(LayoutKind.Sequential)] public class MouseHookStruct { public POINT pt; // 鼠标位置 public int hWnd; public int wHitTestCode; public int dwExtraInfo; } //声明一个Point的封送类型 [StructLayout(LayoutKind.Sequential)] public class POINT { public int x; public int y; } // 装置钩子的函数 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); // 卸下钩子的函数 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern bool UnhookWindowsHookEx(int idHook); // 下一个钩挂的函数 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); #endregion #region 构造(单例模式)与析构函数 private static volatile MouseHook MyMouseHook; private readonly static object createLock = new object(); private MouseHook() { } public static MouseHook GetMouseHook() { if (MyMouseHook == null) { lock (createLock) { if (MyMouseHook == null) { MyMouseHook = new MouseHook(); } } } return MyMouseHook; } /// /// 析构函数 /// ~MouseHook() { Stop(); } #endregion /// /// 启动全局钩子 /// public void Start() { if (isStart) { return; } lock (lockObject) { if (isStart) { return; } if (OnMouseActivity == null) { throw new Exception("Please set handler first!Then run Start"); } // 安装鼠标钩子 if (_hMouseHook == 0) { // 生成一个HookProc的实例. _mouseHookProcedure = new HookProc(MouseHookProc); _hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, _mouseHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0); //假设装置失败停止钩子 if (_hMouseHook == 0) { Stop(); throw new Exception("SetWindowsHookEx failed."); } } isStart = true; } } /// /// 停止全局钩子 /// public void Stop() { if (!isStart) { return; } lock (lockObject) { if (!isStart) { return; } bool retMouse = true; if (_hMouseHook != 0) { retMouse = UnhookWindowsHookEx(_hMouseHook); _hMouseHook = 0; } // 假设卸下钩子失败 if (!(retMouse)) throw new Exception("UnhookWindowsHookEx failed."); // 删除所有事件 OnMouseActivity = null; // 标志位改变 isStart = false; } } /// /// 鼠标钩子回调函数 /// private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam) { // 假设正常执行而且用户要监听鼠标的消息 if ((nCode >= 0) && (OnMouseActivity != null)) { MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); OnMouseActivity(wParam, MyMouseHookStruct); } // 启动下一次钩子 return CallNextHookEx(_hMouseHook, nCode, wParam, lParam); } /// /// 注册全局鼠标事件 /// /// public void AddMouseHandler(MyMouseEventHandler handler) { OnMouseActivity += handler; } /// /// 注销全局鼠标事件 /// /// public void RemoveMouseHandler(MyMouseEventHandler handler) { if (OnMouseActivity != null) { OnMouseActivity -= handler; } } } ``` # 使用 ### 启动监听 ``` MouseHook mouseHook = GetMouseHook(); private void startListenMouse(MyMouseEventHandler handler) { mouseHook.AddMouseHandler(handler); mouseHook.Start(); } ``` ### 停止监听 ``` // 停止钩子 public void stopListenMouse() { mouseHook.Stop(); } ``` ### 监听回调函数 **提示:**虽然有双击判断,但是实际上不生效。要通过两次触发事件的时间间隔进行判断 ``` private void mouseHandler(Int32 wParam, MouseHookStruct mouseMsg) { //taskTxtBx.AppendText("wParam:" + wParam + "\r\n"); switch (wParam) { case WM_MOUSEMOVE: // 鼠标移动 int x = mouseMsg.pt.x; int y = mouseMsg.pt.y; taskTxtBx.AppendText(x + "," + y+"\r\n"); break; case WM_LBUTTONDOWN: // 鼠标左键 taskTxtBx.AppendText("按下鼠标左键\r\n"); break; case WM_LBUTTONUP: taskTxtBx.AppendText("松开鼠标左键\r\n"); break; case WM_LBUTTONDBLCLK: taskTxtBx.AppendText("双击鼠标左键\r\n"); break; case WM_RBUTTONDOWN: taskTxtBx.AppendText("按下鼠标右键\r\n"); break; case WM_RBUTTONUP: taskTxtBx.AppendText("松开鼠标右键\r\n"); break; case WM_RBUTTONDBLCLK: taskTxtBx.AppendText("双击鼠标右键\r\n"); break; case WM_WHEELDOWN: taskTxtBx.AppendText("按下鼠标滑轮\r\n"); break; case WM_WHEELUP: taskTxtBx.AppendText("松开鼠标滑轮\r\n"); break; case WM_WHEELSCROLL: taskTxtBx.AppendText("滚动滑轮\r\n"); if(mouseMsg.hWnd == WM_SCROLLBACK) { taskTxtBx.AppendText("向后滚动滑轮:"+mouseMsg.hWnd + "," + mouseMsg.wHitTestCode + "," + mouseMsg.dwExtraInfo + "\r\n"); }else if(mouseMsg.hWnd == WM_SCROLLFORWARD) { taskTxtBx.AppendText("向前滚动滑轮:" + mouseMsg.hWnd + "," + mouseMsg.wHitTestCode + "," + mouseMsg.dwExtraInfo + "\r\n"); } break; default: taskTxtBx.AppendText("未知操作"+ wParam + "\r\n"); break; } } ``` ### 错误:获取鼠标位置可能错误 详见:https://www.malaoshi.top/show_1IX8FifIbtNn.html ##### 解决: 获取分辨率缩放 ``` float screenScale = ScreenScale.GetScreenScale(); ``` 将鼠标坐标除以缩放比例: ``` case WM_MOUSEMOVE: // 鼠标移动 int x = (int) ( mouseMsg.pt.x / screenScale); int y = (int)(mouseMsg.pt.y / screenScale); taskTxtBx.AppendText("mouse move " + x + "," + y + "\r\n"); break; ``` 参考: https://blog.csdn.net/qq_43851684/article/details/113096306 原文出处:https://malaoshi.top/show_1IX8FexSJ7Km.html