鼠標鍵盤鈎子捕獲(初版)


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

所以自己進行了修改完善,當然,學識有限,還有很多地方需要完善,

歡迎指正!

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

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

  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 }

下面是我的項目截圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM