封装类
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 成员变量、回调函数、事件
/// <summary>
/// 钩子回调函数
/// </summary>
/// <param name="nCode">如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册)</param>
/// <param name="wParam">记录了按下的按钮</param>
/// <param name="lParam"></param>
/// <returns></returns>
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
/// <summary>
/// 全局的鼠标事件
/// </summary>
/// <param name="wParam"> 代表发生的鼠标的事件 </param>
/// <param name="mouseMsg">钩子的结构体,存储着鼠标的位置及其他信息</param>
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
/// <summary>
/// 钩子结构体
/// </summary>
[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;
}
/// <summary>
/// 析构函数
/// </summary>
~MouseHook()
{
Stop();
}
#endregion
/// <summary>
/// 启动全局钩子
/// </summary>
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;
}
}
/// <summary>
/// 停止全局钩子
/// </summary>
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;
}
}
/// <summary>
/// 鼠标钩子回调函数
/// </summary>
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);
}
/// <summary>
/// 注册全局鼠标事件
/// </summary>
/// <param name="handler"></param>
public void AddMouseHandler(MyMouseEventHandler handler)
{
OnMouseActivity += handler;
}
/// <summary>
/// 注销全局鼠标事件
/// </summary>
/// <param name="handler"></param>
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