很久沒寫文章了,還記得年前面試了一家公司,爲了檢測一下個人學習能力,給了我一個任務,作一個自動登陸並自動操做菜單的程序。html
花了幾天的時間研究了Hook以及使用WindowsAPI操做程序的知識,如今記錄一下,也算是一次溫習。面試
一丶Hookide
在我看來Hook就是監測用戶操做鍵盤(或虛擬鍵盤)以及鼠標的行爲,對於Hook的理解我也不是很深刻,也只是一點皮毛。函數
1. 實現Hook的步驟佈局
①安裝鉤子post
②監測鍵盤和鼠標的操做,用來實現相應的邏輯學習
③卸載鉤子ui
2.安裝鉤子this
鉤子分兩種:鍵盤鉤子和鼠標鉤子,而每一種鉤子又能夠分爲全局鉤子或局部勾子。url
下面是安裝鉤子須要的Windows Message常量(網上找的)。
1 public enum HookType : int 2 { 3 /// <summary> 4 /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使咱們能夠監視菜單,滾動 5 ///條,消息框,對話框消息而且發現用戶使用ALT+TAB or ALT+ESC 組合鍵切換窗口。 6 ///WH_MSGFILTER Hook只能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到通 7 ///過安裝了Hook子過程的應用程序創建的對話框的消息。WH_SYSMSGFILTER Hook 8 ///監視全部應用程序消息。 9 /// 10 ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使咱們能夠在模式循環期間 11 ///過濾消息,這等價於在主消息循環中過濾消息。 12 /// 13 ///經過調用CallMsgFilter function能夠直接的調用WH_MSGFILTER Hook。經過使用這 14 ///個函數,應用程序可以在模式循環期間使用相同的代碼去過濾消息,如同在主消息循 15 ///環裏同樣 16 /// </summary> 17 WH_MSGFILTER = -1, 18 /// <summary> 19 /// WH_JOURNALRECORD Hook用來監視和記錄輸入事件。典型的,可使用這 20 ///個Hook記錄連續的鼠標和鍵盤事件,而後經過使用WH_JOURNALPLAYBACK Hook 21 ///來回放。WH_JOURNALRECORD Hook是全局Hook,它不能象線程特定Hook同樣 22 ///使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行 23 ///程地址空間 24 /// </summary> 25 WH_JOURNALRECORD = 0, 26 /// <summary> 27 /// WH_JOURNALPLAYBACK Hook使應用程序能夠插入消息到系統消息隊列。可 28 ///以使用這個Hook回放經過使用WH_JOURNALRECORD Hook記錄下來的連續的鼠 29 ///標和鍵盤事件。只要WH_JOURNALPLAYBACK Hook已經安裝,正常的鼠標和鍵盤 30 ///事件就是無效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象線程特定 31 ///Hook同樣使用。WH_JOURNALPLAYBACK Hook返回超時值,這個值告訴系統在處 32 ///理來自回放Hook當前消息以前須要等待多長時間(毫秒)。這就使Hook能夠控制實 33 ///時事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被 34 ///注射到任何行程地址空間 35 /// </summary> 36 WH_JOURNALPLAYBACK = 1, 37 /// <summary> 38 /// 在應用程序中,WH_KEYBOARD Hook用來監視WM_KEYDOWN and 39 ///WM_KEYUP消息,這些消息經過GetMessage or PeekMessage function返回。可使 40 ///用這個Hook來監視輸入到消息隊列中的鍵盤消息 41 /// </summary> 42 WH_KEYBOARD = 2, 43 /// <summary> 44 /// 應用程序使用WH_GETMESSAGE Hook來監視從GetMessage or PeekMessage函 45 ///數返回的消息。你可使用WH_GETMESSAGE Hook去監視鼠標和鍵盤輸入,以及 46 ///其它發送到消息隊列中的消息 47 /// </summary> 48 WH_GETMESSAGE = 3, 49 /// <summary> 50 /// 監視發送到窗口過程的消息,系統在消息發送到接收窗口過程以前調用 51 /// </summary> 52 WH_CALLWNDPROC = 4, 53 /// <summary> 54 /// 在如下事件以前,系統都會調用WH_CBT Hook子過程,這些事件包括: 55 ///1. 激活,創建,銷燬,最小化,最大化,移動,改變尺寸等窗口事件; 56 ///2. 完成系統指令; 57 ///3. 來自系統消息隊列中的移動鼠標,鍵盤事件; 58 ///4. 設置輸入焦點事件; 59 ///5. 同步系統消息隊列事件。 60 ///Hook子過程的返回值肯定系統是否容許或者防止這些操做中的一個 61 /// </summary> 62 WH_CBT = 5, 63 /// <summary> 64 /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使咱們能夠監視菜單,滾動 65 ///條,消息框,對話框消息而且發現用戶使用ALT+TAB or ALT+ESC 組合鍵切換窗口。 66 ///WH_MSGFILTER Hook只能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到通 67 ///過安裝了Hook子過程的應用程序創建的對話框的消息。WH_SYSMSGFILTER Hook 68 ///監視全部應用程序消息。 69 /// 70 ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使咱們能夠在模式循環期間 71 ///過濾消息,這等價於在主消息循環中過濾消息。 72 /// 73 ///經過調用CallMsgFilter function能夠直接的調用WH_MSGFILTER Hook。經過使用這 74 ///個函數,應用程序可以在模式循環期間使用相同的代碼去過濾消息,如同在主消息循 75 ///環裏同樣 76 /// </summary> 77 WH_SYSMSGFILTER = 6, 78 /// <summary> 79 /// WH_MOUSE Hook監視從GetMessage 或者 PeekMessage 函數返回的鼠標消息。 80 ///使用這個Hook監視輸入到消息隊列中的鼠標消息 81 /// </summary> 82 WH_MOUSE = 7, 83 /// <summary> 84 /// 當調用GetMessage 或 PeekMessage 來從消息隊列種查詢非鼠標、鍵盤消息時 85 /// </summary> 86 WH_HARDWARE = 8, 87 /// <summary> 88 /// 在系統調用系統中與其它Hook關聯的Hook子過程以前,系統會調用 89 ///WH_DEBUG Hook子過程。你可使用這個Hook來決定是否容許系統調用與其它 90 ///Hook關聯的Hook子過程 91 /// </summary> 92 WH_DEBUG = 9, 93 /// <summary> 94 /// 外殼應用程序可使用WH_SHELL Hook去接收重要的通知。當外殼應用程序是 95 ///激活的而且當頂層窗口創建或者銷燬時,系統調用WH_SHELL Hook子過程。 96 ///WH_SHELL 共有5鍾狀況: 97 ///1. 只要有個top-level、unowned 窗口被產生、起做用、或是被摧毀; 98 ///2. 當Taskbar須要重畫某個按鈕; 99 ///3. 當系統須要顯示關於Taskbar的一個程序的最小化形式; 100 ///4. 當目前的鍵盤佈局狀態改變; 101 ///5. 當使用者按Ctrl+Esc去執行Task Manager(或相同級別的程序)。 102 /// 103 ///按照慣例,外殼應用程序都不接收WH_SHELL消息。因此,在應用程序可以接 104 ///收WH_SHELL消息以前,應用程序必須調用SystemParametersInfo function註冊它自 105 ///己 106 /// </summary> 107 WH_SHELL = 10, 108 /// <summary> 109 /// 當應用程序的前臺線程處於空閒狀態時,可使用WH_FOREGROUNDIDLE 110 ///Hook執行低優先級的任務。當應用程序的前臺線程大概要變成空閒狀態時,系統就 111 ///會調用WH_FOREGROUNDIDLE Hook子過程 112 /// </summary> 113 WH_FOREGROUNDIDLE = 11, 114 /// <summary> 115 /// 監視發送到窗口過程的消息,系統在消息發送到接收窗口過程以後調用 116 /// </summary> 117 WH_CALLWNDPROCRET = 12, 118 /// <summary> 119 /// 監視輸入到線程消息隊列中的鍵盤消息 120 /// </summary> 121 WH_KEYBOARD_LL = 13, 122 /// <summary> 123 /// 監視輸入到線程消息隊列中的鼠標消息 124 /// </summary> 125 WH_MOUSE_LL = 14 126 }
而用的最多的也就是:WH_KEYBOARD,WH_MOUSE, WH_KEYBOARD_LL,WH_MOUSE_LL。
WH_KEYBOARD和WH_MOUSE是全局鉤子,而WH_KEYBOARD_LL和WH_MOUSE_LL是針對某個線程的。
因此說安裝全局仍是局部鉤子取決於傳入的消息常量。
安裝鉤子須要調用的API:
1 /// <summary> 2 /// 安裝勾子 3 /// </summary> 4 /// <param name="idHook">鉤子類型,此處用整形的枚舉表示</param> 5 /// <param name="hookCallBack">鉤子發揮做用時的回調函數</param> 6 /// <param name="moudleHandle">應用程序實例的模塊句柄(通常來講是你鉤子回調函數所在的應用程序實例模塊句柄)</param> 7 /// <param name="threadID">與安裝的鉤子子程相關聯的線程的標識符 8 /// <remarks>若是線程ID是0則針對系統級別的,不然是針對當前線程</remarks> 9 /// </param> 10 /// <returns>返回鉤子句柄</returns> 11 [DllImport("user32.dll")] 12 public static extern int SetWindowsHookEx(int idHook, HookProcCallBack hookCallBack, IntPtr moudleHandle, int threadID); 13 14 public delegate int HookProcCallBack(int nCode, int wParam, IntPtr lParam);
☆:上面方法的第二個須要是個委託參數,必須把它設置爲靜態變量,由於監測鉤子至關於一個定時器一直在跑,若是委託變量不是靜態的話,會被GC給回收掉的。
3.監測鍵盤和鼠標行爲
鍵盤操做分爲:keyDown,keyPress,keyUp;鼠標操做分爲:rightClick,leftClick,doubleClick,wheel,move。
因此爲了要監測上面的全部行爲,須要使用事件來實現。
4.卸載鉤子
主要仍是調用API就能夠了。
1 /// <summary> 2 /// 卸載勾子 3 /// </summary> 4 /// <param name="handle">要取消的鉤子的句柄</param> 5 /// <returns>卸載鉤子是否成功</returns> 6 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 7 public static extern bool UnhookWindowsHookEx(int handle);
5.HookManager
發現寫不下去,不知道該講什麼了,不少細節上都沒有講到,好比每一個參數的含義,怎麼調用等等,算是給出個思路,我也是下載了不少源碼摸索過來的。
給出主要實現代碼:
1 using System; 2 using System.ComponentModel; 3 using System.Diagnostics; 4 using System.Runtime.InteropServices; 5 using System.Windows.Forms; 6 7 using SharpCommon.Windows; 8 9 /* 10 11 * 2014-1-28 完善第一個版本 12 * 13 * 1.關於KeyPress的解釋 14 * 在控件有焦點的狀況下按下鍵時發生。 15 * 鍵事件按下列順序發生: 16 KeyDown 17 KeyPress 18 KeyUp 19 20 非字符鍵不會引起 KeyPress 事件;但非字符鍵卻能夠引起 KeyDown 和 KeyUp 事件。 21 使用 KeyChar 屬性在運行時對鍵擊進行取樣,而且使用或修改公共鍵擊的子集。 22 若要僅在窗體級別處理鍵盤事件而不容許其餘控件接收鍵盤事件, 23 * 請將窗體的 KeyPress 事件處理方法中的 KeyPressEventArgs.Handled 屬性設置爲 true。 24 * 25 * 摘自MSDN上的說明 26 * KeyPressEventArgs 指定在用戶按鍵時撰寫的字符。例如,當用戶按 Shift + K 時,KeyChar 屬性返回一個大寫字母 K。 27 當用戶按下任意鍵時,發生 KeyPress 事件。與 KeyPress 事件緊密相關的兩個事件爲 KeyUp 和 KeyDown。 28 * 當用戶按下某個鍵時,KeyDown 事件先於每一個 KeyPress 事件發生;當用戶釋放某個鍵時發生 KeyUp 事件。 29 * 當用戶按住某個鍵時,每次字符重複時,KeyDown 和 KeyPress 事件也都重複發生。一個 KeyUp 事件在釋放按鍵時生成。 30 31 KeyPressEventArgs 隨着 KeyPress 事件的每次發生而被傳遞。 32 * KeyEventArgs 隨着 KeyDown 和 KeyUp 事件的每次發生而被傳遞。 33 * KeyEventArgs 指定是否有任一個組合鍵(Ctrl、Shift 或 Alt)在另外一個鍵按下的同時也曾按下。 34 * 此修飾符信息也能夠經過 Control 類的 ModifierKeys 屬性得到。 35 36 將 Handled 設置爲 true,以取消 KeyPress 事件。這可防止控件處理按鍵。 37 38 注意注意: 39 有些控件將會在 KeyDown 上處理某些擊鍵。 40 * 例如,RichTextBox 在調用 KeyPress 前處理 Enter 鍵。 41 * 在這種狀況下,您沒法取消 KeyPress 事件,而是必須從 KeyDown 取消擊鍵。 42 * 43 * 44 * 2014-1-28 1:00 PM 45 * 1. 完成了對組合鍵的監測代碼,經過獲取KeyState來判斷是否按了組合鍵 46 47 */ 48 49 namespace SharpCommon.Hook 50 { 51 public sealed class HookManager 52 { 53 #region Event And Field 54 public event CustomKeyEventHandler KeyUp; 55 public event CustomKeyEventHandler KeyDown; 56 public event CustomKeyEventHandler KeyPress; 57 58 public event MouseEventHandler MouseMove; 59 public event MouseEventHandler MouseWheel; 60 public event MouseEventHandler LeftMouseClickUp; 61 public event MouseEventHandler RightMouseClickUp; 62 public event MouseEventHandler LeftMouseClickDown; 63 public event MouseEventHandler RightMouseClickDown; 64 public event MouseEventHandler LeftMouseDoubleClick; 65 public event MouseEventHandler RightMouseDoubleClick; 66 67 private static int _mouseHookHandle; 68 private static int _keyboardHookHandlel; 69 70 private static HookProcCallBack _mouseHookCallBack; 71 private static HookProcCallBack _keyboardHookCallBack; 72 73 private static readonly HookManager _instance = new HookManager(); 74 75 private static readonly int _currentThreadID = AppDomain.GetCurrentThreadId(); 76 private static readonly IntPtr _currentMoudleHandle = WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); 77 78 #endregion 79 80 #region Instance 81 82 private HookManager() 83 { } 84 85 public static HookManager Instance 86 { 87 get { return _instance; } 88 } 89 90 #endregion 91 92 #region Install Hook 93 94 /// <summary> 95 /// Install the hook. 96 /// </summary> 97 /// <param name="installType">Select the hook install type.</param> 98 public void InstallHook(HookInstallType installType = HookInstallType.MouseAndKeyBoard) 99 { 100 switch (installType) 101 { 102 case HookInstallType.Mouse: 103 this.InstallMouseHook(); 104 break; 105 case HookInstallType.KeyBoard: 106 this.InstallKeyBoardHook(); 107 break; 108 case HookInstallType.MouseAndKeyBoard: 109 this.InstallMouseHook(); 110 this.InstallKeyBoardHook(); 111 break; 112 } 113 } 114 115 #endregion 116 117 #region Mouse Hook Monitor 118 119 /// <summary> 120 /// Install the mouse hook. 121 /// Default install mouse global hook - [14]; 122 /// </summary> 123 /// <param name="hookType">Select mouse hook type.</param> 124 public void InstallMouseHook(HookType hookType = HookType.WH_MOUSE_LL) 125 { 126 if (_mouseHookHandle == default(int)) 127 { 128 _mouseHookCallBack = new HookProcCallBack(this.MouseHookCallBack); 129 if (hookType == HookType.WH_MOUSE) 130 { 131 _mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, IntPtr.Zero, _currentThreadID); 132 } 133 else 134 { 135 _mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, _currentMoudleHandle, 0); 136 } 137 this.CheckHandleIsZero(_mouseHookHandle); 138 } 139 } 140 141 private int MouseHookCallBack(int nCode, int wParam, IntPtr lParam) 142 { 143 MouseButtons mouseOperation = MouseButtons.None; 144 Point mousePoint = (Point)Marshal.PtrToStructure(lParam, typeof(Point)); 145 146 switch (wParam) 147 { 148 case (int)WindowsMessage.WM_LBUTTONDOWN: 149 mouseOperation = MouseButtons.Left; 150 this.InvokeMouseEvent(this.LeftMouseClickDown, mouseOperation, mousePoint); 151 break; 152 case (int)WindowsMessage.WM_LBUTTONUP: 153 mouseOperation = MouseButtons.Left; 154 this.InvokeMouseEvent(this.LeftMouseClickUp, mouseOperation, mousePoint); 155 break; 156 case (int)WindowsMessage.WM_LBUTTONDBLCLK: 157 mouseOperation = MouseButtons.Left; 158 this.InvokeMouseEvent(this.LeftMouseDoubleClick, mouseOperation, mousePoint); 159 break; 160 case (int)WindowsMessage.WM_RBUTTONDOWN: 161 mouseOperation = MouseButtons.Right; 162 this.InvokeMouseEvent(this.RightMouseClickDown, mouseOperation, mousePoint); 163 break; 164 case (int)WindowsMessage.WM_RBUTTONUP: 165 mouseOperation = MouseButtons.Right; 166 this.InvokeMouseEvent(this.RightMouseClickUp, mouseOperation, mousePoint); 167 break; 168 case (int)WindowsMessage.WM_RBUTTONDBLCLK: 169 mouseOperation = MouseButtons.Right; 170 this.InvokeMouseEvent(this.RightMouseDoubleClick, mouseOperation, mousePoint); 171 break; 172 case (int)WindowsMessage.WM_MOUSEMOVE: 173 this.InvokeMouseEvent(this.MouseMove, mouseOperation, mousePoint); 174 break; 175 case (int)WindowsMessage.WM_MOUSEWHEEL: 176 this.InvokeMouseEvent(this.MouseWheel, mouseOperation, mousePoint); 177 break; 178 } 179 180 return HookAPI.CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam); 181 } 182 183 private void InvokeMouseEvent(MouseEventHandler mouseEvent, MouseButtons mouseButton, Point point) 184 { 185 if (mouseEvent != null) 186 { 187 MouseEventArgs mouseArgs = new MouseEventArgs(mouseButton, 0, point.X, point.Y, 0); 188 mouseEvent(this, mouseArgs); 189 } 190 } 191 192 #endregion 193 194 #region KeyBoaed Hook Monitor 195 196 /// <summary> 197 /// Install the keyboard hook. 198 /// Default install keyboard global hook - [13]. 199 /// </summary> 200 /// <param name="hookType">Select keyboard hook type.</param> 201 public void InstallKeyBoardHook(HookType hookType = HookType.WH_KEYBOARD_LL) 202 { 203 if (_keyboardHookHandlel == default(int)) 204 { 205 _keyboardHookCallBack = new HookProcCallBack(this.KeyBoradHookCallBack); 206 if (hookType == HookType.WH_KEYBOARD) 207 { 208 _keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, IntPtr.Zero, _currentThreadID); 209 } 210 else 211 { 212 _keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, _currentMoudleHandle, 0); 213 } 214 this.CheckHandleIsZero(_keyboardHookHandlel); 215 } 216 } 217 218 private int KeyBoradHookCallBack(int nCode, int wParam, IntPtr lParam) 219 { 220 if (nCode >= 0) 221 { 222 CustomKeyBoard keyInfo = (CustomKeyBoard)Marshal.PtrToStructure(lParam, typeof(CustomKeyBoard)); 223 224 if (this.KeyDown != null 225 && (wParam == (int)WindowsMessage.WM_KEYDOWN || wParam == (int)WindowsMessage.WM_SYSKEYDOWN)) 226 { 227 this.InvokeKeyBoardEvent(this.KeyDown, (Keys)keyInfo.VirtualKeyCode); 228 } 229 230 if (this.KeyPress != null && wParam == (int)WindowsMessage.WM_KEYDOWN) 231 { 232 this.InvokeKeyBoardEvent(this.KeyPress, (Keys)keyInfo.VirtualKeyCode); 233 } 234 235 if (this.KeyUp != null 236 && (wParam == (int)WindowsMessage.WM_KEYUP || wParam == (int)WindowsMessage.WM_SYSKEYUP)) 237 { 238 this.InvokeKeyBoardEvent(this.KeyUp, (Keys)keyInfo.VirtualKeyCode); 239 } 240 } 241 242 return HookAPI.CallNextHookEx(_keyboardHookHandlel, nCode, wParam, lParam); 243 } 244 245 private void InvokeKeyBoardEvent(CustomKeyEventHandler keyEvent, Keys keyData) 246 { 247 CustomKeyEventArgs customKeyArgs = new CustomKeyEventArgs(keyData); 248 keyEvent(this, customKeyArgs); 249 } 250 251 #endregion 252 253 #region Common 254 255 private void CheckHandleIsZero(int handle) 256 { 257 if (handle == 0) 258 { 259 int errorID = Marshal.GetLastWin32Error(); 260 throw new Win32Exception(errorID); 261 } 262 } 263 264 public void UninstallHook() 265 { 266 if (_mouseHookHandle != default(int)) 267 { 268 if (HookAPI.UnhookWindowsHookEx(_mouseHookHandle)) 269 { 270 _mouseHookHandle = default(int); 271 } 272 } 273 if (_keyboardHookHandlel != default(int)) 274 { 275 if (HookAPI.UnhookWindowsHookEx(_keyboardHookHandlel)) 276 { 277 _keyboardHookHandlel = default(int); 278 } 279 } 280 } 281 282 #endregion 283 } 284 }
所有代碼:下載
好了就這麼多了,已同步至:我的文章目錄索引