由于VSTO本身没有提供充分的鼠标键盘事件,在制作Add-in的时候非常不方便,迫于无奈想到使用Hook来辅助一下,大部分网上参考文章都只是展示了全局钩子的写法,而线程钩子的写法和介绍相对少一些,特别是关键语句上如果定义的不正确是没有任何效果的,在自己反复尝试后决定留下一个正确的版本分享出来,毕竟全局钩子性能差,没有办法用到VSTO中的。
1 public class Win32API
2 {
3 #region 导入API
4
5 /// <summary>
6 /// 安装钩子
7 /// </summary>
8 [DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
9 public static extern IntPtr SetWindowsHookEx(WH_CODE idHook, HookProc lpfn, IntPtr pInstance, uint threadId);
10
11 /// <summary>
12 /// 卸载钩子
13 /// </summary>
14 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
15 public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
16
17 /// <summary>
18 /// 传递钩子
19 /// </summary>
20 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
21 public static extern int CallNextHookEx(IntPtr pHookHandle, int nCodem, Int32 wParam, IntPtr lParam);
22
23 /// <summary>
24 /// 获取全部按键状态
25 /// </summary>
26 /// <param name="pbKeyState"></param>
27 /// <returns>非0表示成功</returns>
28 [DllImport("user32.dll")]
29 public static extern int GetKeyboardState(byte[] pbKeyState);
30
31 /// <summary>
32 /// 获取程序集模块的句柄
33 /// </summary>
34 /// <param name="lpModuleName"></param>
35 /// <returns></returns>
36 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
37 public static extern IntPtr GetModuleHandle(string lpModuleName);
38
39 /// <summary>
40 /// 获取指定句柄的线程ID
41 /// </summary>
42 /// <param name="hwnd"></param>
43 /// <param name="ID"></param>
44 /// <returns></returns>
45 [DllImport("user32.dll", CharSet = CharSet.Auto)]
46 public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
47
48 /// <summary>
49 /// 获取当前进程中的当前线程ID
50 /// </summary>
51 /// <returns></returns>
52 [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
53 public static extern uint GetCurrentThreadId();
54
55 /// <summary>
56 /// 获取指定按键状态
57 /// </summary>
58 /// <param name="keyCode"></param>
59 /// <returns></returns>
60 [DllImport("user32.dll", CharSet = CharSet.Auto)]
61 public static extern int GetKeyState(int keyCode);
62
63 #endregion
64 }
1 #region 定义结构
2
3 public enum WH_CODE : int
4 {
5 WH_JOURNALRECORD = 0,
6 WH_JOURNALPLAYBACK = 1,
7
8 /// <summary>
9 /// 进程钩子
10 /// </summary>
11 WH_KEYBOARD = 2,
12
13 WH_GETMESSAGE = 3,
14 WH_CALLWNDPROC = 4,
15 WH_CBT = 5,
16 WH_SYSMSGFILTER = 6,
17 /// <summary>
18 /// 进程钩子
19 /// </summary>
20 WH_MOUSE = 7,
21 WH_HARDWARE = 8,
22 WH_DEBUG = 9,
23 WH_SHELL = 10,
24 WH_FOREGROUNDIDLE = 11,
25 WH_CALLWNDPROCRET = 12,
26 /// <summary>
27 /// 底层键盘钩子
28 /// </summary>
29 WH_KEYBOARD_LL = 13,
30
31 /// <summary>
32 /// 底层鼠标钩子
33 /// </summary>
34 WH_MOUSE_LL = 14
35 }
36
37 public enum WM_MOUSE : int
38 {
39 /// <summary>
40 /// 鼠标开始
41 /// </summary>
42 WM_MOUSEFIRST = 0x200,
43
44 /// <summary>
45 /// 鼠标移动
46 /// </summary>
47 WM_MOUSEMOVE = 0x200,
48
49 /// <summary>
50 /// 左键按下
51 /// </summary>
52 WM_LBUTTONDOWN = 0x201,
53
54 /// <summary>
55 /// 左键释放
56 /// </summary>
57 WM_LBUTTONUP = 0x202,
58
59 /// <summary>
60 /// 左键双击
61 /// </summary>
62 WM_LBUTTONDBLCLK = 0x203,
63
64 /// <summary>
65 /// 右键按下
66 /// </summary>
67 WM_RBUTTONDOWN = 0x204,
68
69 /// <summary>
70 /// 右键释放
71 /// </summary>
72 WM_RBUTTONUP = 0x205,
73
74 /// <summary>
75 /// 右键双击
76 /// </summary>
77 WM_RBUTTONDBLCLK = 0x206,
78
79 /// <summary>
80 /// 中键按下
81 /// </summary>
82 WM_MBUTTONDOWN = 0x207,
83
84 /// <summary>
85 /// 中键释放
86 /// </summary>
87 WM_MBUTTONUP = 0x208,
88
89 /// <summary>
90 /// 中键双击
91 /// </summary>
92 WM_MBUTTONDBLCLK = 0x209,
93
94 /// <summary>
95 /// 滚轮滚动
96 /// </summary>
97 /// <remarks>WINNT4.0以上才支持此消息</remarks>
98 WM_MOUSEWHEEL = 0x020A
99 }
100
101 public enum WM_KEYBOARD : int
102 {
103 /// <summary>
104 /// 非系统按键按下
105 /// </summary>
106 WM_KEYDOWN = 0x100,
107
108 /// <summary>
109 /// 非系统按键释放
110 /// </summary>
111 WM_KEYUP = 0x101,
112
113 /// <summary>
114 /// 系统按键按下
115 /// </summary>
116 WM_SYSKEYDOWN = 0x104,
117
118 /// <summary>
119 /// 系统按键释放
120 /// </summary>
121 WM_SYSKEYUP = 0x105
122 }
123
124 public enum HC_CODE : int
125 {
126 HC_ACTION = 0,
127 HC_GETNEXT = 1,
128 HC_SKIP = 2,
129 HC_NOREMOVE = 3,
130 HC_NOREM = 3,
131 HC_SYSMODALON = 4,
132 HC_SYSMODALOFF = 5
133 }
134
135 public enum VK_CODE : int
136 {
137 VK_LBUTTON = 0x01,
138 VK_RBUTTON = 0x02,
139 VK_SHIFT = 0x10,
140 VK_CONTROL = 0x11,
141 VK_MENU = 0x12,//ALT
142 VK_C = 0x43,
143 VK_V = 0x56,
144 VK_X = 0x58,
145 VK_Y = 0x59,
146 VK_Z = 0x5A,
147 VK_APPS = 0x5D,
148 VK_LSHIFT = 0xA0,
149 VK_RSHIFT = 0xA1,
150 VK_LCONTROL = 0xA2,
151 VK_RCONTROL = 0xA3,
152 VK_LMENU = 0xA4,
153 VK_RMENU = 0xA5
154 }
155
156 /// <summary>
157 /// 键盘钩子事件结构定义
158 /// </summary>
159 /// <remarks>详细说明请参考MSDN中关于 KBDLLHOOKSTRUCT 的说明</remarks>
160 [StructLayout(LayoutKind.Sequential)]
161 public struct KeyboardHookStruct
162 {
163 /// <summary>
164 /// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
165 /// </summary>
166 public UInt32 VKCode;
167
168 /// <summary>
169 /// Specifies a hardware scan code for the key.
170 /// </summary>
171 public UInt32 ScanCode;
172
173 /// <summary>
174 /// Specifies the extended-key flag, event-injected flag, context code,
175 /// and transition-state flag. This member is specified as follows.
176 /// An application can use the following values to test the keystroke flags.
177 /// </summary>
178 public UInt32 Flags;
179
180 /// <summary>
181 /// Specifies the time stamp for this message.
182 /// </summary>
183 public UInt32 Time;
184
185 /// <summary>
186 /// Specifies extra information associated with the message.
187 /// </summary>
188 public UInt32 ExtraInfo;
189 }
190
191 [StructLayout(LayoutKind.Sequential)]
192 public struct POINT
193 {
194 public int X;
195 public int Y;
196 }
197
198 /// <summary>
199 /// 鼠标钩子事件结构定义
200 /// </summary>
201 /// <remarks>详细说明请参考MSDN中关于 MSLLHOOKSTRUCT 的说明</remarks>
202 [StructLayout(LayoutKind.Sequential)]
203 public struct MouseHookStruct
204 {
205 public POINT Point;
206 public uint hwnd;
207 public uint wHitTestCode;
208 public uint dwExtraInfo;
209 }
210
211 #endregion
1 public class Hook
2 {
3 #region 私有变量
4
5 private byte[] mKeyState = new byte[256];
6 private Keys mKeyData = Keys.None; //专门用于判断按键的状态
7
8 /// <summary>
9 /// 键盘钩子句柄
10 /// </summary>
11 private IntPtr mKetboardHook = IntPtr.Zero;
12 /// <summary>
13 /// 鼠标钩子句柄
14 /// </summary>
15 private IntPtr mMouseHook = IntPtr.Zero;
16 /// <summary>
17 /// 键盘钩子委托实例
18 /// </summary>
19 private HookProc mKeyboardHookProcedure;
20 /// <summary>
21 /// 鼠标钩子委托实例
22 /// </summary>
23 private HookProc mMouseHookProcedure;
24
25 #endregion
26
27 #region 鼠标事件
28
29 public event MouseEventHandler OnMouseDown;
30 public event MouseEventHandler OnMouseUp;
31 public event MouseEventHandler OnMouseMove;
32
33 #endregion
34
35 #region 键盘事件
36
37 public event KeyEventHandler OnKeyDown;
38 public event KeyEventHandler OnKeyUp;
39
40 #endregion
41
42 /// <summary>
43 /// 构造函数
44 /// </summary>
45 public Hook()
46 {
47 Win32API.GetKeyboardState(this.mKeyState);
48 }
49
50 ~Hook()
51 {
52 UnInstallHook();
53 }
54
55 /// <summary>
56 /// 键盘钩子处理函数
57 /// </summary>
58 private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
59 {
60 // 定义为线程钩子时,wParam的值是击打的按键,与Keys里的对应按键相同
61 if ((nCode == (int)HC_CODE.HC_ACTION) && (this.OnKeyDown != null || this.OnKeyUp != null))
62 {
63 //使用全局Hook才需要
64 //KeyboardHookStruct KeyboardInfo = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
65
66 mKeyData = (Keys)wParam;
67 KeyEventArgs keyEvent = new KeyEventArgs(mKeyData);
68 //这里简单的通过lParam的值的正负情况与按键的状态相关联
69 if (lParam.ToInt32() > 0 && this.OnKeyDown != null)
70 {
71 this.OnKeyDown(this, keyEvent);
72 }
73 else if (lParam.ToInt32() < 0 && this.OnKeyUp != null)
74 {
75 this.OnKeyUp(this, keyEvent);
76 }
77 }
78
79 return Win32API.CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam);
80 }
81
82 /// <summary>
83 /// 鼠标钩子处理函数
84 /// </summary>
85 private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
86 {
87 if ((nCode == (int)HC_CODE.HC_ACTION))
88 {
89 MouseHookStruct mouseInfo = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
90 MouseEventArgs mouseEvent;
91 if (wParam == (int)WM_MOUSE.WM_LBUTTONDOWN)
92 {
93 mouseEvent = new MouseEventArgs(MouseButtons.Left, 1, mouseInfo.Point.X, mouseInfo.Point.Y, 0);
94 if (this.OnMouseDown != null)
95 {
96 this.OnMouseDown(this, mouseEvent);
97 }
98 }
99 else if (wParam == (int)WM_MOUSE.WM_RBUTTONDOWN)
100 {
101 mouseEvent = new MouseEventArgs(MouseButtons.Right, 1, mouseInfo.Point.X, mouseInfo.Point.Y, 0);
102 if (this.OnMouseDown != null)
103 {
104 this.OnMouseDown(this, mouseEvent);
105 }
106 }
107 else if (wParam == (int)WM_MOUSE.WM_LBUTTONUP)
108 {
109 mouseEvent = new MouseEventArgs(MouseButtons.Left, 1, mouseInfo.Point.X, mouseInfo.Point.Y, 0);
110 if (this.OnMouseUp != null)
111 {
112 this.OnMouseUp(this, mouseEvent);
113 }
114 }
115 else if (wParam == (int)WM_MOUSE.WM_RBUTTONUP)
116 {
117 mouseEvent = new MouseEventArgs(MouseButtons.Right, 1, mouseInfo.Point.X, mouseInfo.Point.Y, 0);
118 if (this.OnMouseUp != null)
119 {
120 this.OnMouseUp(this, mouseEvent);
121 }
122 }
123 else if (wParam == (int)WM_MOUSE.WM_MOUSEMOVE)
124 {
125 mouseEvent = new MouseEventArgs(MouseButtons.None, 0, mouseInfo.Point.X, mouseInfo.Point.Y, 0);
126 if (this.OnMouseMove != null)
127 {
128 this.OnMouseMove(this, mouseEvent);
129 }
130 }
131 }
132
133 return Win32API.CallNextHookEx(this.mMouseHook, nCode, wParam, lParam);
134 }
135
136 /// <summary>
137 /// 安装钩子
138 /// </summary>
139 /// <returns></returns>
140 public bool InstallHook()
141 {
142 //线程钩子时一定要通过这个取得的值才是操作系统下真实的线程
143 uint result = Win32API.GetCurrentThreadId();
144
145 #region 全局Hook
146 //IntPtr pInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);
147 //using (Process curProcess = Process.GetCurrentProcess())
148 //{
149 // using (ProcessModule curModule = curProcess.MainModule)
150 // {
151 // pInstance = Win32API.GetModuleHandle(curModule.ModuleName);
152 // }
153 //}
154 #endregion
155
156 if (this.mKetboardHook == IntPtr.Zero)
157 {
158 this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
159 //注册线程钩子时第三个参数是空
160 this.mKetboardHook = Win32API.SetWindowsHookEx(WH_CODE.WH_KEYBOARD, this.mKeyboardHookProcedure, IntPtr.Zero, result);
161 if (this.mKetboardHook == IntPtr.Zero)
162 {
163 return false;
164 }
165 }
166 if (this.mMouseHook == IntPtr.Zero)
167 {
168 this.mMouseHookProcedure = new HookProc(this.MouseHookProc);
169 //注册线程钩子时第三个参数是空
170 this.mMouseHook = Win32API.SetWindowsHookEx(WH_CODE.WH_MOUSE, this.mMouseHookProcedure, IntPtr.Zero, result);
171 if (this.mMouseHook == IntPtr.Zero)
172 {
173 this.UnInstallHook();
174 return false;
175 }
176 }
177
178 return true;
179 }
180
181 /// <summary>
182 /// 卸载钩子
183 /// </summary>
184 /// <returns></returns>
185 public bool UnInstallHook()
186 {
187 bool result = true;
188 if (this.mKetboardHook != IntPtr.Zero)
189 {
190 result = Win32API.UnhookWindowsHookEx(this.mKetboardHook) && result;
191 this.mKetboardHook = IntPtr.Zero;
192 }
193 if (this.mMouseHook != IntPtr.Zero)
194 {
195 result = (Win32API.UnhookWindowsHookEx(this.mMouseHook) && result);
196 this.mMouseHook = IntPtr.Zero;
197 }
198
199 return result;
200 }
201 }