鼠標鍵盤鉤子捕獲(第一版)

興趣使然,想找到C#鼠標鍵盤鉤子代碼在網上查找資料發現C#鼠標鉤子的資料不太完善,在實際操做過程當中發現有些效果也不太理想,數組

因此本身進行了修改完善,固然,學識有限,還有不少地方須要完善,函數

歡迎指正!學習

這個項目不能直接啓動,須要使用Ctrl+F5非調試啓動或者直接啓動bin文件下編譯程序纔不出錯this

:這個只是學習參考,可別動壞腦筋spa

  1 using System;
  2 
  3 using System.Runtime.InteropServices;
  4 using System.Windows.Forms;
  5 using System.Reflection;
  6 
  7 namespace autoInput.Support
  8 {
  9     /// <summary>
 10     /// 鍵盤鉤子
 11     /// [如下代碼來自某網友,並由「Murphy丶悅」改造完善]
 12     /// [僅供參考學習,請勿進行非法操做]
 13     /// </summary>
 14     public class KeyboardHook
 15     {
 16         public event KeyEventHandler KeyDownEvent;
 17         public event KeyPressEventHandler KeyPressEvent;
 18         public event KeyEventHandler KeyUpEvent;
 19         public event MouseEventHandler MouseDownDouble;
 20         public event MouseEventHandler MouseDown;
 21         public event MouseEventHandler MouseDownRight;
 22         public event MouseEventHandler Mousemov;
 23         public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
 24         static int hKeyboardHook = 0; //聲明鍵盤鉤子處理的初始值
 25         static int hMouseHook = 0;//申明鼠標鉤子處理的初始值
 26         //值在Microsoft SDK的Winuser.h裏查詢
 27         public const int WH_KEYBOARD_LL = 13;   //線程鍵盤鉤子監聽鼠標消息設爲2,全局鍵盤監聽鼠標消息設爲13
 28         HookProc KeyboardHookProcedure; //聲明KeyboardHookProcedure做爲HookProc類型
 29         HookProc MouseHookProceduremz; //聲明MouseHookProcedure做爲HookProc類型
 30 
 31         //鍵盤結構
 32         [StructLayout(LayoutKind.Sequential)]
 33         public class KeyboardHookStruct
 34         {
 35             public int vkCode;  //定一個虛擬鍵碼。該代碼必須有一個價值的範圍1至254
 36             public int scanCode; // 指定的硬件掃描碼的關鍵
 37             public int flags;  // 鍵標誌
 38             public int time; // 指定的時間戳記的這個訊息
 39             public int dwExtraInfo; // 指定額外信息相關的信息
 40         }
 41         //使用此功能,安裝了一個鉤子
 42         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 43         public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
 44 
 45 
 46         //調用此函數卸載鉤子
 47         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 48         public static extern bool UnhookWindowsHookEx(int idHook);
 49 
 50 
 51         //使用此功能,經過信息鉤子繼續下一個鉤子
 52         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 53         public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
 54 
 55         // 取得當前線程編號(線程鉤子須要用到)
 56         [DllImport("kernel32.dll")]
 57         static extern int GetCurrentThreadId();
 58 
 59         //使用WINDOWS API函數代替獲取當前實例的函數,防止鉤子失效
 60         [DllImport("kernel32.dll")]
 61         public static extern IntPtr GetModuleHandle(string name);
 62 
 63         //引入系統的雙擊時間
 64         [DllImport("user32.dll")]
 65         public static extern int GetDoubleClickTime();
 66 
 67         public void Start()
 68         {
 69             //安裝鼠標鉤子
 70             if (hMouseHook == 0)
 71             {
 72                 MouseHookProceduremz = new HookProc(MouseHookProcedure);
 73                 hMouseHook = SetWindowsHookEx(14, MouseHookProceduremz, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
 74                 if (hMouseHook == 0)
 75                 {
 76                     Stopmouse();
 77                     MessageBox.Show("安裝鼠標鉤子失敗");
 78                 }
 79             }
 80             // 安裝鍵盤鉤子
 81             if (hKeyboardHook == 0)
 82             {
 83                 KeyboardHookProcedure = new HookProc(KeyboardHookProc);
 84                 hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
 85                 // hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
 86                 //鍵盤線程鉤子
 87                 // SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero,GetCurrentThreadId());
 88                 //指定要監聽的線程idGetCurrentThreadId(),
 89                 //鍵盤全局鉤子,須要引用空間(using System.Reflection;)`
 90                 //關於SetWindowsHookEx (int idHook, HookProc lpfn, IntPtrhInstance, int threadId)函數將鉤子加入到鉤子鏈表中,說明一下四個參數:
 91                 //idHook 鉤子類型,即肯定鉤子監聽何種消息,上面的代碼中設爲2,即監聽鍵盤消息而且是線程鉤子,若是是全局鉤子監聽鍵盤消息應設爲13,
 92                 //線程鉤子監聽鼠標消息設爲7,全局鉤子監聽鼠標消息設爲14。
 93                 //lpfn鉤子子程的地址指針。若是dwThreadId參數爲0 或是一個由別的進程建立的線程的標識,lpfn必須指向DLL中的鉤子子程。 除此之外,
 94                 //lpfn能夠指向當前進程的一段鉤子子程代碼。鉤子函數的入口地址,當鉤子鉤到任何消息後便調用這個函數。hInstance應用程序實例的句柄。
 95                 //標識包含lpfn所指的子程的DLL。若是threadId 標識當前進程建立的一個線程,並且子
 96                 //程代碼位於當前進程,hInstance必須爲NULL。能夠很簡單的設定其爲本應用程序的實例句柄。threadId 與安裝的鉤子子程相關聯的線程的標識符
 97                 //若是爲0,鉤子子程與全部的線程關聯,即爲全局鉤子
 98                 //若是SetWindowsHookEx失敗
 99                 if (hKeyboardHook == 0)
100                 {
101                     Stop();
102                     MessageBox.Show("安裝鍵盤鉤子失敗");
103                 }
104             }
105         }
106         private void Stopmouse()
107         {
108             bool retKeyboard = true;
109             if (hMouseHook != 0)
110             {
111                 retKeyboard = UnhookWindowsHookEx(hMouseHook);
112                 hMouseHook = 0;
113             }
114             if (!(retKeyboard)) MessageBox.Show("卸載鼠標鉤子失敗");
115         }
116         #region 鼠標鉤子結構體
117         //聲明一個Point的封送類型 
118         [StructLayout(LayoutKind.Sequential)]
119         public struct POINT
120         {
121             public int x;
122             public int y;
123         }
124         //聲明鼠標鉤子的封送結構類型 
125         [StructLayout(LayoutKind.Sequential)]
126         public struct MouseHookStruct
127         {
128             public POINT pt;
129             public int hWnd;
130             public int wHitTestCode;
131             public int dwExtraInfo;
132         }
133         //定義鼠標常數
134         private const int WM_MOUSEMOVE = 0x200;//鼠標移動 512(int)
135         private const int WM_LBUTTONDOWN = 0x201;//鼠標左鍵 513(int)
136         private const int WM_RBUTTONDOWN = 0x204;//鼠標右鍵 516(int)
137         private const int WM_MBUTTONDOWN = 0x207;//鼠標中健 519(int)
138         private const int WM_LBUTTONUP = 0x202;//左鍵彈起 514(int)
139         private const int WM_RBUTTONUP = 0x205;//右鍵彈起 517(int)
140         private const int WM_MBUTTONUP = 0x208;//中健彈起 520(int)
141         private const int WM_LBUTTONDBLCLK = 0x203;//雙擊左鍵 515(int)
142         private const int WM_RBUTTONDBLCLK = 0x206;//雙擊右鍵 518(int)
143         private const int WM_MBUTTONDBLCLK = 0x209;//雙擊中健 521(int)
144 
145         //對於鼠標雙擊的動做捕獲,都是認爲能夠捕獲WM_LBUTTONDBLCLK這個消息,經過該消息來設定是否爲雙擊,
146         //但實際掛在鉤子後,Windows並不會直接捕獲到WM_LBUTTONDBLCLK這個消息,而是兩次WM_LBUTTONDOWN消息,
147         //所以,若是要實現雙擊的捕獲,可考慮計算兩次點擊的時間間隔,和系統的雙擊時間間隔進行比較,從而斷定是否爲雙擊。
148 
149         //在鼠標鉤子中右鍵雙擊兩次能夠捕獲到兩次單擊右鍵,
150         //可是在這個類拿到消息並進行鼠標常數更改成雙擊後在窗口中只能顯示出單擊一次
151         //另在各種資料中顯示的中鍵鼠標常數所有沒法捕獲到
152         #endregion
153         //記錄初始時間進行時間復位
154         private static int InitialTime = 0;//對單擊兩次後時間復位,防止無限斷定雙擊事件
155         //上一次單擊對應按鍵時間
156         private static int LeftClickTime = 0;//上一次單擊左鍵時間
157         private static int RightClickTime = 0;//上一次單擊右鍵時間
158         private static int MiddleClickTime = 0;//上一次單擊中鍵時間
159         //上一次單擊事件座標
160         private int lastX = 0;//上一次鼠標X軸位置
161         private int lastY = 0;//上一次鼠標Y軸位置
162         public int i = 0;
163         private int MouseHookProcedure(int nCode, int wParam, IntPtr lParam)
164         {
165             int clickCount = 0;
166             if ((nCode >= 0) && (MouseDown != null || MouseDownDouble != null || Mousemov != null || MouseDownRight != null))
167             {
168                 MouseButtons button = MouseButtons.None;
169 
170                 MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
171                 int X = MyMouseHookStruct.pt.x;//當前單擊事件X軸座標
172                 int Y = MyMouseHookStruct.pt.y;//當前單擊事件Y軸座標
173 
174                 //進行時間比對;手動對鼠標按鍵命令更改
175                 if (wParam == WM_LBUTTONDOWN)//對單擊左鍵進行斷定
176                     wParam = changeGorge(wParam, LeftClickTime, WM_LBUTTONDBLCLK, X, Y);
177                 if(wParam== WM_RBUTTONDOWN)//對單擊右鍵進行斷定
178                     wParam= changeGorge(wParam, RightClickTime, WM_RBUTTONDBLCLK, X, Y);
179                 if (wParam == WM_MBUTTONDOWN)//對單擊中鍵進行斷定
180                     wParam = changeGorge(wParam, MiddleClickTime, WM_MBUTTONDBLCLK, X, Y);
181 
182                 switch (wParam)
183                 {
184                     //左鍵按下
185                     case WM_LBUTTONDOWN:
186                         button = MouseButtons.Left;
187                         clickCount = 1;
188                         break;
189                     //左鍵擡起
190                     case WM_LBUTTONUP:
191                         button = MouseButtons.Left;
192                         clickCount = 1;
193                         break;
194                     //雙擊左鍵
195                     case WM_LBUTTONDBLCLK:
196                         button = MouseButtons.Left;
197                         clickCount = 2;
198                         break;
199                     //中鍵按下
200                     case WM_MBUTTONDOWN:
201                         button = MouseButtons.Middle;
202                         clickCount = 1;
203                         break;
204                     //中鍵擡起
205                     case WM_MBUTTONUP:
206                         button = MouseButtons.Middle;
207                         clickCount = 1;
208                         break;
209                     //雙擊中鍵
210                     case WM_MBUTTONDBLCLK:
211                         button = MouseButtons.Middle;
212                         clickCount = 2;
213                         break;
214                     //右鍵按下
215                     case WM_RBUTTONDOWN:
216                         button = MouseButtons.Right;
217                         clickCount = 1;
218                         break;
219                     //右鍵擡起
220                     case WM_RBUTTONUP:
221                         button = MouseButtons.Right;
222                         clickCount = 1;
223                         break;
224                     //雙擊右鍵
225                     case WM_RBUTTONDBLCLK:
226                         button = MouseButtons.Right;
227                         clickCount = 2;
228                         break;
229                     case WM_MOUSEMOVE:
230                         clickCount = 0;
231                         break;
232                 }
233 
234                 MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
235 
236                 //raise KeyDown;
237                 if (MouseDown != null && wParam == WM_LBUTTONDOWN)//單擊事件
238                 {
239                     MouseDown(this, e);
240                 }
241                 if (this.MouseDownDouble != null && wParam == WM_LBUTTONDBLCLK)//雙擊事件
242                 {
243                     MouseDownDouble(this, e);
244                 }
245                 if (this.Mousemov != null && wParam == WM_LBUTTONDOWN && wParam == WM_MOUSEMOVE) //拖動事件
246                 {
247                     Mousemov(this, e);
248                 }
249                 if (this.MouseDownRight != null && wParam == WM_RBUTTONDOWN)//右擊事件
250                 {
251                     MouseDownRight(this, e);
252                 }
253             }
254 
255             //若是返回1,則結束消息,這個消息到此爲止,再也不傳遞。
256             //若是返回0或調用CallNextHookEx函數則消息出了這個鉤子繼續往下傳遞,也就是傳給消息真正的接受者 
257             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
258         }
259 
260         public void Stop()
261         {
262             bool retKeyboard = true;
263             if (hKeyboardHook != 0)
264             {
265                 retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
266                 hKeyboardHook = 0;
267             }
268             if (!(retKeyboard)) MessageBox.Show("卸載鍵盤鉤子失敗");
269         }
270         //ToAscii職能的轉換指定的虛擬鍵碼和鍵盤狀態的相應字符或字符
271         [DllImport("user32")]
272         public static extern int ToAscii(int uVirtKey, //[in] 指定虛擬關鍵代碼進行翻譯。
273                                          int uScanCode, // [in] 指定的硬件掃描碼的關鍵須翻譯成英文。高階位的這個值設定的關鍵,若是是(不壓)
274                                          byte[] lpbKeyState, // [in] 指針,以256字節數組,包含當前鍵盤的狀態。每一個元素(字節)的數組包含狀態的一個關鍵。若是高階位的字節是一套,關鍵是下跌(按下)。在低比特,若是設置代表,關鍵是對切換。在此功能,只有肘位的CAPS LOCK鍵是相關的。在切換狀態的NUM個鎖和滾動鎖定鍵被忽略。
275                                          byte[] lpwTransKey, // [out] 指針的緩衝區收到翻譯字符或字符。
276                                          int fuState); // [in] Specifieswhether a menu is active. This parameter must be 1 if a menu is active, or 0otherwise.
277 
278         //獲取按鍵的狀態
279         [DllImport("user32")]
280         public static extern int GetKeyboardState(byte[] pbKeyState);
281 
282 
283         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
284         private static extern short GetKeyState(int vKey);
285 
286         private const int WM_KEYDOWN = 0x100;//KEYDOWN
287         private const int WM_KEYUP = 0x101;//KEYUP
288         private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN
289         private const int WM_SYSKEYUP = 0x105;//SYSKEYUP
290 
291         private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
292         {
293             // 偵聽鍵盤事件
294             if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
295             {
296                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
297                 // raise KeyDown
298                 if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
299                 {
300                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
301                     KeyEventArgs e = new KeyEventArgs(keyData);
302                     KeyDownEvent(this, e);
303                 }
304 
305                 //鍵盤按下
306                 if (KeyPressEvent != null && wParam == WM_KEYDOWN)
307                 {
308                     byte[] keyState = new byte[256];
309                     GetKeyboardState(keyState);
310 
311                     byte[] inBuffer = new byte[2];
312                     if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
313                     {
314                         KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
315                         KeyPressEvent(this, e);
316                     }
317                 }
318 
319                 // 鍵盤擡起
320                 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
321                 {
322                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
323                     KeyEventArgs e = new KeyEventArgs(keyData);
324                     KeyUpEvent(this, e);
325                 }
326 
327             }
328             //若是返回1,則結束消息,這個消息到此爲止,再也不傳遞。
329             //若是返回0或調用CallNextHookEx函數則消息出了這個鉤子繼續往下傳遞,也就是傳給消息真正的接受者
330             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
331         }
332 
333         /// <summary>
334         /// 對單擊事件進行斷定是否爲雙擊事件並進行修改
335         /// </summary>
336         /// <param name="wParams">串口內的單擊事件消息</param>
337         /// <param name="lastClickTime">上一次單擊事件事件</param>
338         /// <param name="doubleClick">正確雙擊事件消息</param>
339         /// <param name="X">上一次單擊X軸座標</param>
340         /// <param name="Y">上一次單擊Y軸座標</param>
341         /// <returns></returns>
342         public int changeGorge(int wParam,int lastClickTime,int doubleClick,int X,int Y)
343         {
344             int deltaMs = GetDoubleClickTime() + lastClickTime;
345             if (deltaMs >= dateTimeToInt(DateTime.Now) && lastX == X && lastY == Y)//在系統雙擊斷定時間內同一座標單擊兩次更改成雙擊事件
346             {
347                 wParam = doubleClick;
348                 if (lastClickTime == LeftClickTime)
349                     LeftClickTime = InitialTime;
350                 else if (lastClickTime == RightClickTime)
351                     RightClickTime = InitialTime;
352                 else if (lastClickTime == MiddleClickTime)
353                     MiddleClickTime = InitialTime;
354             }
355             else
356             {
357                 if (lastClickTime == LeftClickTime)
358                 {
359                     LeftClickTime = dateTimeToInt(DateTime.Now);
360                     RightClickTime = InitialTime;
361                     MiddleClickTime = InitialTime;
362                 }
363                 else if (lastClickTime == RightClickTime)
364                 {
365                     RightClickTime = dateTimeToInt(DateTime.Now);
366                     LeftClickTime = InitialTime;
367                     MiddleClickTime = InitialTime;
368                 }
369                 else if (lastClickTime == MiddleClickTime)
370                 {
371                     MiddleClickTime = dateTimeToInt(DateTime.Now);
372                     LeftClickTime = InitialTime;
373                     RightClickTime = InitialTime;
374                 }
375             }
376             lastX = X;
377             lastY = Y;
378             return wParam;
379         }
380         /// <summary>
381         /// 轉換時間格式爲HHmmssfff
382         /// </summary>
383         /// <param name="time">需轉換的時間</param>
384         /// <returns></returns>
385         private int dateTimeToInt(DateTime time)
386         {
387             //轉換時間格式爲int類型並精確到毫秒
388             return Convert.ToInt32(time.ToString("HHmmssfff"));
389         }
390         ~KeyboardHook()
391         {
392             Stop();
393         }
394     }
395 }

下面是顯示界面,使用的是WinFrom窗口,在窗口內用一個文本框和一個定時器線程

 1 using System;
 2 using System.Windows.Forms;
 3 
 4 using autoInput.Support;
 5 
 6 namespace autoInput
 7 {
 8     public partial class autoInput : Form
 9     {
10         public autoInput()
11         {
12             InitializeComponent();
13         }
14 
15         KeyboardHook k_hook;
16         private void autoInput_Load(object sender, EventArgs e)
17         {
18             k_hook = new KeyboardHook();
19             k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//全局按鍵事件
20             k_hook.MouseDown += k_hook_MouseDown;//全局鼠標單擊事件
21             k_hook.MouseDownDouble += k_hook_MouseDown;//雙擊事件
22             k_hook.Mousemov += k_hook_MouseDown;//拖動事件
23             k_hook.MouseDownRight += k_hook_MouseDown;//右擊事件
24             k_hook.Start();//安裝鍵盤鉤子
25         }
26 
27         public string button = string.Empty;
28         public string keyboardRecordText = string.Empty;
29         //單擊事件
30         void k_hook_MouseDown(object sender, MouseEventArgs e) 
31         {
32             if (keyboardRecordText != string.Empty && e.Button.ToString() != button)
33                 showText();
34             button = e.Button.ToString();
35             keyboardRecordText=string.Format("Button:{0} Clicks:{1} \tX:{2} Y:{3} \t{4} \n", e.Button, e.Clicks, e.X, e.Y, DateTime.Now.ToString("HH:mm:ss.fff"));
36 
37             if (e.Clicks == 2)
38             {
39                 showText();
40                 keyboardRecordText = string.Empty;
41 
42             }
43             else
44             {
45                 this.timer.Enabled = true;
46                 timer.Interval = KeyboardHook.GetDoubleClickTime();
47             }
48         }
49 
50         /// <summary>
51         /// 鍵盤事件
52         /// </summary>
53         /// <param name="sender"></param>
54         /// <param name="e"></param>
55         private void hook_KeyDown(object sender, KeyEventArgs e)
56         {
57             if (e.KeyValue == (int)Keys.Space)
58             {
59                 keyboardRecord.Text += " ";
60             }
61             else if (e.KeyValue == (int)Keys.Enter)
62             {
63                 keyboardRecord.Text += "\n";
64             }
65             else
66             {
67                 keyboardRecord.Text += e.KeyData.ToString();
68             }
69         }
70 
71         /// <summary>
72         /// 定時器
73         /// </summary>
74         /// <param name="sender"></param>
75         /// <param name="e"></param>
76         private void timer_Tick(object sender, EventArgs e)
77         {
78             showText();
79             this.timer.Enabled = false;
80         }
81         public void showText()
82         {
83             //keyboardRecord窗口程序內文本框的名稱
84             keyboardRecord.Text += keyboardRecordText;
85             keyboardRecordText = string.Empty;
86         }
87     }
88 }

下面是個人項目截圖翻譯

相關文章
相關標籤/搜索