windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数,根据不同的消息完成不同的功能。而消息钩子是windows提供的一种消息过滤和预处理机制,可以用来截获和监视系统中的消息。按照钩子作用范围不同,又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL文件中实现相应的钩子函数。
由于windows操作是基于消息的,那么鼠标和键盘的操作也是通过消息传递到目标窗口的,因此可以按装全局鼠标键盘钩子,使鼠标键盘在传递到目标窗口之前拦截、钩住它,为我们提供一个做操作的机会,可以触发某个事件方法,可以阻止它传递、也可以不做任何处理。
不管是鼠标钩子还是键盘钩子,都需要先注册windows全局钩子。
方法:
首先需要调用user32.dll包
SetWindowsHookEx:安装全局钩子
UnhookWindowsHookEx:卸载全局钩子
CallNextHookEx:调用下一个钩子
参数:
private static int hMouseHook = 0;
private const int WM_MOUSEMOVE = 0x200; //鼠标移动,本次没有使用这个参数,而是用的MouseEventArgs()方法监听鼠标移动 private const int WM_LBUTTONDOWN = 0x201; //左键按下 private const int WM_RBUTTONDOWN = 0x204; //右键按下 private const int WM_MBUTTONDOWN = 0x207; //中键按下 private const int WM_LBUTTONUP = 0x202; //左键抬起 private const int WM_RBUTTONUP = 0x205; //右键抬起 private const int WM_MBUTTONUP = 0x208; //中键抬起
private const int WM_LBUTTONDBLCLK = 0x203; //左键单击,本次未使用 private const int WM_RBUTTONDBLCLK = 0x206; //右键单击,本次未使用 private const int WM_MBUTTONDBLCLK = 0x209; //中键单击,本次未使用
完整流程:
第一步、定义windows全局钩子类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Win32Api
2 {
3
4 public delegate int HookProc( int nCode, IntPtr wParam, IntPtr lParam);
5
6 ///
7 /// 安装钩子。把一个应用程序定义的钩子子程安装到钩子链表中。函数成功则返回钩子子程的句柄,失败返回NULL
8 ///
9 /// 钩子的类型。它决定了 HOOKPROC 被调用的时机
10 /// 指向钩子回调函数的指针。如果最后一个参数 dwThreadId 为0或者是其它进程创建的线程标识符,则 lpfn 参数必须指向DLL中的钩子回调函数,即 HOOKPROC 函数必须在DLL中实现。否则,lpfn 可以指向与当前进程相关联的代码中的钩子过程
11 /// 包含由 lpfn 参数指向的钩子回调函数的DLL句柄。如果 dwThreadId 参数指定由当前进程创建线程,并且钩子回调函数位于当前进程关联的代码中,则 hmod 参数必须设置为 NULL。
12 /// 与钩子程序关联的线程标识符(指定要 HOOK 的线程 ID)。如果此参数为0,则钩子过程与系统中所有线程相关联,即全局消息钩子
13 ///
14 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
15 public static extern int SetWindowsHookEx(int idHook,HookProc lpfn,IntPtr hInstance, int threadId);
16
17 ///
18 /// 卸载钩子。函数成功则返回非0,失败返回NULL
19 ///
20 /// 钩子的类型
21 ///
22 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
23 public static extern bool UnhookWindowsHookEx(int idHook);
24
25 ///
26 /// 调用下一个钩子。调用钩子链中的下一个挂钩过程,调用成功返回值是下一个钩子的回调函数,否则为0。当前钩子程序也必须返回此值。
27 ///
28 /// 钩子的类型
29 /// 钩子代码。就是给下一个钩子要交待的内容
30 /// 要传递的参数。由钩子类型决定是什么参数
31 /// 要传递的参数。由钩子类型决定是什么参数
32 ///
33 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
34 public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
35
36 }
View Code
注:鼠标钩子函数和键盘钩子函数都会公用上面这个全局钩子类
第二步、定义鼠标钩子方法和键盘钩子方法
鼠标钩子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class MouseHook
2 {
3 private Point point;
4 private Point Point
5 {
6 get { return point; }
7 set
8 {
9 if (point != value)
10 {
11 point = value;
12 if (MouseMoveEvent != null)
13 {
14 var e = new MouseEventArgs(MouseButtons.None, 0, point.X, point.Y, 0);
15 MouseMoveEvent(this, e);
16 }
17 }
18 }
19 }
20 private int hHook;
21 private static int hMouseHook = 0;
22 private const int WM_MOUSEMOVE = 0x200;
23 private const int WM_LBUTTONDOWN = 0x201;
24 private const int WM_RBUTTONDOWN = 0x204;
25 private const int WM_MBUTTONDOWN = 0x207;
26 private const int WM_LBUTTONUP = 0x202;
27 private const int WM_RBUTTONUP = 0x205;
28 private const int WM_MBUTTONUP = 0x208;
29 private const int WM_LBUTTONDBLCLK = 0x203;
30 private const int WM_RBUTTONDBLCLK = 0x206;
31 private const int WM_MBUTTONDBLCLK = 0x209;
32
33 public const int WH_MOUSE_LL = 14; //idHook值的参数,14为系统级,截获全局鼠标消息。详细SetWindowsHookEx函数的idHook参照https://www.cnblogs.com/ndyxb/p/12883292.html
34 public Win32Api.HookProc hProc;
35 public MouseHook()
36 {
37 this.Point = new Point();
38 }
39
40 ///
41 /// 安装鼠标钩子
42 ///
43 ///
44 public int SetHook()
45 {
46 hProc = new Win32Api.HookProc(MouseHookProc);
47 hHook = Win32Api.SetWindowsHookEx(WH_MOUSE_LL, hProc, IntPtr.Zero, 0);
48 return hHook;
49 }
50
51 ///
52 /// 卸载鼠标钩子
53 ///
54 public void UnHook()
55 {
56 Win32Api.UnhookWindowsHookEx(hHook);
57 }
58
59 ///
60 /// 执行鼠标钩子
61 ///
62 ///
63 ///
64 ///
65 ///
66 private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
67 {
68 MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
69 if (nCode < 0)
70 {
71 return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
72 }
73 else
74 {
75 MouseButtons button = MouseButtons.None;
76 int clickCount = 0;
77 switch ((Int32)wParam)
78 {
79 case WM_LBUTTONDOWN:
80 button = MouseButtons.Left;
81 clickCount = 1;
82 MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
83 break;
84 case WM_RBUTTONDOWN:
85 button = MouseButtons.Right;
86 clickCount = 1;
87 MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
88 break;
89 case WM_MBUTTONDOWN:
90 button = MouseButtons.Middle;
91 clickCount = 1;
92 MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
93 break;
94 case WM_LBUTTONUP:
95 button = MouseButtons.Left;
96 clickCount = 1;
97 MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
98 break;
99 case WM_RBUTTONUP:
100 button = MouseButtons.Right;
101 clickCount = 1;
102 MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
103 break;
104 case WM_MBUTTONUP:
105 button = MouseButtons.Middle;
106 clickCount = 1;
107 MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
108 break;
109 }
110
111 this.Point = new Point(MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);
112 return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
113 }
114 }
115
116
117 [StructLayout(LayoutKind.Sequential)]
118 public class POINT
119 {
120 public int x;
121
122 public int y;
123
124 }
125 [StructLayout(LayoutKind.Sequential)]
126 public class MouseHookStruct
127 {
128
129 public POINT pt;
130
131 public int hwnd;
132
133 public int wHitTestCode;
134
135 public int dwExtraInfo;
136
137 }
138
139
140
141
142 public delegate void MouseMoveHandler(object sender, MouseEventArgs e);
143 public event MouseMoveHandler MouseMoveEvent;
144 public delegate void MouseDownHandler(object sender, MouseEventArgs e);
145 public event MouseDownHandler MouseDownEvent;
146 public delegate void MouseUpHandler(object sender, MouseEventArgs e);
147 public event MouseUpHandler MouseUpEvent;
148 }
View Code
键盘钩子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class KeyHook
2 {
3 public event KeyEventHandler KeyDownEvent;
4 public event KeyPressEventHandler KeyPressEvent;
5 public event KeyEventHandler KeyUpEvent;
6
7
8 static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
9 //值在Microsoft SDK的Winuser.h里查询
10 public const int WH_KEYBOARD_LL = 13; //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
11 Win32Api.HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
12 //键盘结构
13
14 [StructLayout(LayoutKind.Sequential)]
15 public class KeyboardHookStruct
16 {
17 public int vkCode; //定一个虚拟键码。该代码必须有一个价值的范围1至254
18 public int scanCode; // 指定的硬件扫描码的关键
19 public int flags; // 键标志
20 public int time; // 指定的时间戳记的这个讯息
21 public int dwExtraInfo; // 指定额外信息相关的信息
22 }
23
24
25 // 取得当前线程编号(线程钩子需要用到)
26 [DllImport("kernel32.dll")]
27 static extern int GetCurrentThreadId();
28
29 //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
30 [DllImport("kernel32.dll")]
31 public static extern IntPtr GetModuleHandle(string name);
32
33 ///
34 /// 安装键盘钩子
35 ///
36 public void Start()
37 {
38 // 安装键盘钩子
39 if (hKeyboardHook == 0)
40 {
41 KeyboardHookProcedure = new Win32Api.HookProc(KeyboardHookProc);
42 hKeyboardHook = Win32Api.SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
43 //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
44 //************************************
45 //键盘线程钩子
46 Win32Api.SetWindowsHookEx(13, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
47 //键盘全局钩子,需要引用空间(using System.Reflection;)
48 //如果SetWindowsHookEx失败
49 if (hKeyboardHook == 0)
50 {
51 Stop();
52 throw new Exception("安装键盘钩子失败");
53 }
54 }
55 }
56
57 ///
58 /// 卸载键盘钩子
59 ///
60 public void Stop()
61 {
62 bool retKeyboard = true;
63
64
65 if (hKeyboardHook != 0)
66 {
67 retKeyboard = Win32Api.UnhookWindowsHookEx(hKeyboardHook);
68 hKeyboardHook = 0;
69 }
70
71 if (!(retKeyboard)) throw new Exception("卸载钩子失败!");
72 }
73 //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
74
75
76
77
78 [DllImport("user32")]
79 public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
80 int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)
81 byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
82 byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
83 int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
84 //获取按键的状态
85 [DllImport("user32")]
86 public static extern int GetKeyboardState(byte[] pbKeyState);
87
88 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
89 private static extern short GetKeyState(int vKey);
90
91
92 ///
93 /// 执行键盘钩子
94 ///
95 ///
96 ///
97 ///
98 ///
99 private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
100 {
101 // 侦听键盘事件
102 if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
103 {
104 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
105 // 键盘按下
106 if (KeyDownEvent != null && ((Int32)wParam == (Int32)KeyEvent.WM_KEYDOWN || (Int32)wParam == (Int32)KeyEvent.WM_SYSKEYDOWN))
107 {
108 Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
109 KeyEventArgs e = new KeyEventArgs(keyData);
110 KeyDownEvent(this, e);
111 }
112
113 //键盘点击
114 if (KeyPressEvent != null && (Int32)wParam == (Int32)KeyEvent.WM_KEYDOWN)
115 {
116 byte[] keyState = new byte[256];
117 GetKeyboardState(keyState);
118
119 byte[] inBuffer = new byte[2];
120 if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
121 {
122 KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
123 KeyPressEvent(this, e);
124 }
125 }
126
127 // 键盘抬起
128 if (KeyUpEvent != null && ((Int32)wParam == (Int32)KeyEvent.WM_KEYUP || (Int32)wParam == (Int32)KeyEvent.WM_SYSKEYUP))
129 {
130 Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
131 KeyEventArgs e = new KeyEventArgs(keyData);
132 KeyUpEvent(this, e);
133 }
134
135 }
136 //如果返回1,则结束消息,这个消息到此为止,不再传递。
137 //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
138 return Win32Api.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
139 }
140
141 ~KeyHook()
142 {
143 Stop();
144 }
145
146
147 }
View Code
第三步、点击调用鼠标键盘钩子:
点击调用鼠标钩子
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //鼠标事件监听
2 public partial class MouseEventHook
3 {
4 MouseHook mh; //定义全局的鼠标监听方法
5 Point p1 = new Point(0, 0); //定义全局鼠标坐标
6 Point p2 = new Point(0, 0); //多定义个坐标可以显示鼠标按下与抬起之间移动的距离,这个也可不要
7
8 //点击按钮开始鼠标监听
9 private void button1_Click(object sender, EventArgs e)
10 {
11 mh = new MouseHook();
12 mh.SetHook(); //安装鼠标钩子
13
14 mh.MouseMoveEvent += my_MouseMoveEvent; //绑定鼠标移动时触发的事件
15 mh.MouseDownEvent += my_MouseDownEvent; //绑定鼠标按下时触发的事件
16 mh.MouseUpEvent += my_MouseUpEvent; //绑定鼠标抬起时触发的事件
17 }
18
19
20 //点击按钮停止鼠标监听
21 private void button2_Click(object sender, EventArgs e)
22 {
23 if (mh != null) mh.UnHook();
24 MessageBox.Show("鼠标监听停止!");
25 }
26
27
28
29 // 鼠标移动触发的事件
30 private void my_MouseMoveEvent(object
31 sender,MouseEventArgs e)
32 {
33 Point p = e.Location; //获取坐标
34 string movexy = p.ToString();
35 richTextBox1.AppendText(movexy + "," + stopWatch.ElapsedMilliseconds + "\n"); //将坐标记录到RichTextBox控件中
36 }
37
38
39 //按下鼠标键触发的事件
40 private void my_MouseDownEvent(object sender, MouseEventArgs e)
41 {
42 if (e.Button == MouseButtons.Left)
43 {
44 LeftTag = true;
45 richTextBox1.AppendText("按下了左键\n"); //将按键操作记录到RichTextBox控件中
46 }
47 if (e.Button == MouseButtons.Right)
48 {
49 RightTag = true;
50 richTextBox1.AppendText("按下了右键\n");
51 }
52 p1 = e.Location;
53
54 }
55
56
57 //松开鼠标键触发的事件
58 private void my_MouseUpEvent(object sender, MouseEventArgs e)
59 {
60 p2 = e.Location;
61 double value = Math.Sqrt(Math.Abs(p1.X - p2.X) * Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y) * Math.Abs(p1.Y - p2.Y));
62 if (e.Button == MouseButtons.Left)
63 {
64 richTextBox1.AppendText("松开了左键 " + LineNum + "\n");
65
66 }
67 if (e.Button == MouseButtons.Right)
68 {
69 richTextBox1.AppendText("松开了右键 " + LineNum + "\n");
70 }
71 richTextBox1.AppendText("移动了" + value + "距离\n");
72 RightTag = false;
73 LeftTag = false;
74 p1 = new Point(0, 0);
75 p2 = new Point(0, 0);
76 }
77
78
79
80 }
View Code
点击调用键盘钩子
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public partial class KeyEventHook
2 {
3 KeyHook k_hook;
4 KeyEventHandler myKeyDownHandeler;
5 KeyEventHandler myKeyUpHandeler;
6
7 ///
8 /// 开始键盘监听
9 ///
10 public void startKeyListen()
11 {
12 k_hook = new KeyHook();
13 myKeyDownHandeler = new KeyEventHandler(hook_KeyDown);
14 k_hook.KeyDownEvent += myKeyDownHandeler;//钩住键盘按下
15 myKeyUpHandeler = new KeyEventHandler(hook_KeyUp);
16 k_hook.KeyUpEvent += myKeyUpHandeler;//钩住键盘抬起
17 k_hook.Start();//安装键盘钩子
18 }
19
20 ///
21 /// 结束键盘监听
22 ///
23 public void stopKeyListen()
24 {
25 if (myKeyDownHandeler != null)
26 {
27 k_hook.KeyDownEvent -= myKeyDownHandeler;//取消按键事件
28 myKeyDownHandeler = null;
29 k_hook.Stop();//关闭键盘钩子
30 }
31 }
32
33
34
35 ///
36 /// 键盘按下时就调用这个
37 ///
38 ///
39 ///
40 public void hook_KeyDown(object sender, KeyEventArgs e)
41 {
42 if (e.KeyCode.Equals(Keys.Escape)) //如果按下Esc键可执行if里面的操作
43 {
44 //按下特定键后执行的代码
45 }
46 Log.LogMouseEvent(e.KeyCode.ToString() + ",键盘按下" ); //这个Log.LogMouseEvent是我用来记录键盘按下抬起的日志,
47
48 }
49
50 ///
51 /// 有键盘抬起时就调用这个
52 ///
53 ///
54 ///
55 public void hook_KeyUp(object sender, KeyEventArgs e)
56 {
57 Log.LogMouseEvent(e.KeyCode.ToString() + ",键盘抬起"); //这个Log.LogMouseEvent是我用来记录键盘按下抬起的日志
58
59 }
60 }
View Code
总结:
监听流程为:安装钩子——》触发钩子后调用相应的绑定事件——》调用下一个钩子——》监听完成,卸载钩子
钩子在程序退出之后会自动卸载,不过那样容易出错而且不好控制,最好还是手动卸载
END
|