興趣使然,想找到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 }
下面是個人項目截圖翻譯