本文介紹了如何在程式中成功使用C#添加了Hook,供大家參考。
目前的程式中想要添加Hook,截獲鍵盤按鍵訊息,所以上網找了一下關於C# Hook的東西。發現很多人都在說在添加C#
Hook不成功和不穩定,而建議使用C++封一個Dll給C#使用。可喜的是最後我還是成功的在程式中使用C#添加了Hook,經過測試還是沒有什麼問題
的。
進行Hook需要使用三個API函數
SetWindowsHookEx 進行Hook的註冊
UnhookWindowsHookEx 取消已經註冊的Hook
CallNextHookEx 將訊息傳遞給下一個Hook函數
看一下定義
# [DllImport("user32.dll")]<br /># private static extern IntPtr SetWindowsHookEx(<br /># HookType code, HookProc func, IntPtr instance, int threadID);<br />#<br /># [DllImport("user32.dll")]<br /># private static extern int UnhookWindowsHookEx(IntPtr hook);<br />#<br /># [DllImport("user32.dll")]<br /># private static extern int CallNextHookEx(<br /># IntPtr hook, int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
定義兩個event提供給外界使用
# public delegate void HookEventHandler(object sender, HookEventArgs e);<br /># public event HookEventHandler KeyDown;<br /># public event HookEventHandler KeyUp;
因為要接收的是鍵盤的訊息所以要定義一些訊息,和我們要接收的結構。
1. public class HookEventArgs : EventArgs<br /> 2. {<br /> 3. // using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps<br /> 4. // to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not<br /> 5. public Keys Key;<br /> 6. public bool Alt;<br /> 7. public bool Control;<br /> 8. public bool Shift;<br /> 9.<br /> 10. public HookEventArgs(UInt32 keyCode)<br /> 11. {<br /> 12. // detect what modifier keys are pressed, using<br /> 13. // Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers<br /> 14. // since Keyboard.Modifiers does not correctly get the state of the<br /> 15. // modifier keys when the application does not have focus<br /> 16. this.Key = (Keys)keyCode;<br /> 17. this.Alt = (System.Windows.Forms.Control.ModifierKeys & Keys.Alt) != 0;<br /> 18. this.Control = (System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0;<br /> 19. this.Shift = (System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0;<br /> 20. }<br /> 21. }<br /> 22.<br /> 23. private enum HookType : int<br /> 24. {<br /> 25. WH_JOURNALRECORD = 0,<br /> 26. WH_JOURNALPLAYBACK = 1,<br /> 27. WH_KEYBOARD = 2,<br /> 28. WH_GETMESSAGE = 3,<br /> 29. WH_CALLWNDPROC = 4,<br /> 30. WH_CBT = 5,<br /> 31. WH_SYSMSGFILTER = 6,<br /> 32. WH_MOUSE = 7,<br /> 33. WH_HARDWARE = 8,<br /> 34. WH_DEBUG = 9,<br /> 35. WH_SHELL = 10,<br /> 36. WH_FOREGROUNDIDLE = 11,<br /> 37. WH_CALLWNDPROCRET = 12,<br /> 38. WH_KEYBOARD_LL = 13,<br /> 39. WH_MOUSE_LL = 14<br /> 40. }<br /> 41.<br /> 42. public struct KBDLLHOOKSTRUCT<br /> 43. {<br /> 44. public UInt32 vkCode;<br /> 45. public UInt32 scanCode;<br /> 46. public UInt32 flags;<br /> 47. public UInt32 time;<br /> 48. public IntPtr extraInfo;<br /> 49. }
關鍵的在這裡註冊C# Hook
1. private void Install()<br /> 2. {<br /> 3. if (_hookHandle != IntPtr.Zero)<br /> 4. return;<br /> 5.<br /> 6. Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();<br /> 7. System.Diagnostics.Debug.Assert(list != null && list.Length > 0);<br /> 8.<br /> 9. _hookHandle = SetWindowsHookEx(_hookType,<br /> 10. _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);<br /> 11.<br /> 12. }
哦,還有HookType _hookType = HookType.WH_KEYBOARD_LL; 因為要截獲鍵盤訊息
還有_hookFunction = new HookProc(HookCallback);
其實最關鍵的是Marshal.GetHINSTANCE(list[0])得到當前程式的instance,這樣這個C# Hook就是全域的C# Hook,這個位置也可以是null,這樣就不是全域C# Hook。
有個很奇怪的現象就是這個函數,在調試狀態執行不能成功,而做成Release以後運行成功,所以你在做程式時,不要因為調試失敗而對這個函數有懷疑,編一個Release版本的程式,獨立運行再試一下。我使用的環境是VS2005 + Vista。
接收訊息的函數
1. private int HookCallback(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)<br /> 2. {<br /> 3. if (code < 0)<br /> 4. return CallNextHookEx(_hookHandle, code, wParam, ref lParam);<br /> 5.<br /> 6. if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)<br /> 7. this.KeyUp(this, new HookEventArgs(lParam.vkCode));<br /> 8.<br /> 9. if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)<br /> 10. {<br /> 11. this.KeyDown(this, new HookEventArgs(lParam.vkCode));<br /> 12. if (lParam.vkCode == 44)<br /> 13. {<br /> 14. return 1;<br /> 15. }<br /> 16. }<br /> 17.<br /> 18. return CallNextHookEx(_hookHandle, code, wParam, ref lParam);<br /> 19. }
這裡會區分KeyUp和KeyDown,注意一定要調用CallNextHookEx,這樣會將這個訊息在系統中繼續傳遞,這很重要。除非你想阻止這個訊息,也不希望其他程式再處理這個訊息。
下面最後的操作,釋放註冊。
1. private void Uninstall()<br /> 2. {<br /> 3. if (_hookHandle != IntPtr.Zero)<br /> 4. {<br /> 5. UnhookWindowsHookEx(_hookHandle);<br /> 6. _hookHandle = IntPtr.Zero;<br /> 7. }<br /> 8. }