看看C# Hook 如何實(shí)現(xiàn)
目前的程序中想要添加Hook,截獲鍵盤按鍵消息,所以上網(wǎng)找了一下關(guān)于C# Hook的東西。發(fā)現(xiàn)很多人都在說在添加C# Hook不成功和不穩(wěn)定,而建議使用C++封一個(gè)Dll給C#使用??上驳氖亲詈笪疫€是成功的在程序中使用C#添加了Hook,經(jīng)過測(cè)試還是沒有什么問題的。
進(jìn)行Hook需要使用三個(gè)API函數(shù)
SetWindowsHookEx 進(jìn)行Hook的注冊(cè)
UnhookWindowsHookEx 取消已經(jīng)注冊(cè)的Hook
CallNextHookEx 將消息傳遞給下一個(gè)Hook函數(shù)
看一下定義
- [DllImport("user32.dll")]
 - private static extern IntPtr SetWindowsHookEx(
 - HookType code, HookProc func, IntPtr instance, int threadID);
 - [DllImport("user32.dll")]
 - private static extern int UnhookWindowsHookEx(IntPtr hook);
 - [DllImport("user32.dll")]
 - private static extern int CallNextHookEx(
 - IntPtr hook, int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
 
我們需要定義一個(gè)delegate來接收消息
- private delegate int HookProc(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
 
定義兩個(gè)event提供給外界使用
- public delegate void HookEventHandler(object sender, HookEventArgs e);
 - public event HookEventHandler KeyDown;
 - public event HookEventHandler KeyUp;
 
因?yàn)橐邮盏氖擎I盤的消息所以要定義一些消息,和我們要接收的結(jié)構(gòu)。
- public class HookEventArgs : EventArgs
 - {
 - // using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps
 - // to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not
 - public Keys Key;
 - public bool Alt;
 - public bool Control;
 - public bool Shift;
 - public HookEventArgs(UInt32 keyCode)
 - {
 - // detect what modifier keys are pressed, using
 - // Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers
 - // since Keyboard.Modifiers does not correctly get the state of the
 - // modifier keys when the application does not have focus
 - this.Key = (Keys)keyCode;
 - this.Alt = (System.Windows.Forms.Control.ModifierKeys & Keys.Alt) != 0;
 - this.Control = (System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0;
 - this.Shift = (System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0;
 - }
 - }
 - private enum HookType : int
 - {
 - WH_JOURNALRECORD = 0,
 - WH_JOURNALPLAYBACK = 1,
 - WH_KEYBOARD = 2,
 - WH_GETMESSAGE = 3,
 - WH_CALLWNDPROC = 4,
 - WH_CBT = 5,
 - WH_SYSMSGFILTER = 6,
 - WH_MOUSE = 7,
 - WH_HARDWARE = 8,
 - WH_DEBUG = 9,
 - WH_SHELL = 10,
 - WH_FOREGROUNDIDLE = 11,
 - WH_CALLWNDPROCRET = 12,
 - WH_KEYBOARD_LL = 13,
 - WH_MOUSE_LL = 14
 - }
 - public struct KBDLLHOOKSTRUCT
 - {
 - public UInt32 vkCode;
 - public UInt32 scanCode;
 - public UInt32 flags;
 - public UInt32 time;
 - public IntPtr extraInfo;
 - }
 
關(guān)鍵的在這里注冊(cè)C# Hook
- private void Install()
 - {
 - if (_hookHandle != IntPtr.Zero)
 - return;
 - Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
 - System.Diagnostics.Debug.Assert(list != null && list.Length > 0);
 - _hookHandle = SetWindowsHookEx(_hookType,
 - _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
 - }
 
哦,還有HookType _hookType = HookType.WH_KEYBOARD_LL; 因?yàn)橐孬@鍵盤消息
還有_hookFunction = new HookProc(HookCallback);
其實(shí)最關(guān)鍵的是Marshal.GetHINSTANCE(list[0])得到當(dāng)前程序的instance,這樣這個(gè)C# Hook就是全局的C# Hook,這個(gè)位置也可以是null,這樣就不是全局C# Hook。
有個(gè)很奇怪的現(xiàn)象就是這個(gè)函數(shù),在調(diào)試狀態(tài)執(zhí)行不能成功,而做成Release以后運(yùn)行成功,所以你在做程序時(shí),不要因?yàn)檎{(diào)試失敗而對(duì)這個(gè)函數(shù)有懷疑,編一個(gè)Release版本的程序,獨(dú)立運(yùn)行再試一下。我使用的環(huán)境是VS2005 + Vista。
接收消息的函數(shù)
- private int HookCallback(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
 - {
 - if (code < 0)
 - return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
 - if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)
 - this.KeyUp(this, new HookEventArgs(lParam.vkCode));
 - if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)
 - {
 - this.KeyDown(this, new HookEventArgs(lParam.vkCode));
 - if (lParam.vkCode == 44)
 - {
 - return 1;
 - }
 - }
 - return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
 - }
 
這里會(huì)區(qū)分KeyUp和KeyDown,注意一定要調(diào)用CallNextHookEx,這樣會(huì)將這個(gè)消息在系統(tǒng)中繼續(xù)傳遞,這很重要。除非你想阻止這個(gè)消息,也不希望其他程序再處理這個(gè)消息。
下面最后的操作,釋放注冊(cè)。
- private void Uninstall()
 - {
 - if (_hookHandle != IntPtr.Zero)
 - {
 - UnhookWindowsHookEx(_hookHandle);
 - _hookHandle = IntPtr.Zero;
 - }
 - }
 
Ok,完成了。
【編輯推薦】















 
 
 
 
 
 
 