簡介.NET似乎都沒有比較好用的Hook類庫,那就只好自己來做一個。
設計概念
原先我只是想將Hook的API做一些簡單的封裝,將其封裝成一個Hook類,做一做發現其實Hook其實也不多,那乾脆先用enum將所有的hook type(參考:http://msdn.microsoft.com/en-us/library/ms644959%28v=vs.85%29.aspx)一併聲明好了
因此我利用Dictionary資料結構,Key為自定的HookType,Value為自行封裝的Hook類,並用一個HookManager來管理Dictionary,對Hook做Register跟Unregister的動作。
同時在register時提供自訂的delegate,以實踐自訂的hookproc。
public enum HookType{ WH_MSGFILTER = -1, 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_DEBUG = 9, WH_SHELL = 10, WH_FOREGROUNDIDLE = 11, WH_CALLWNDPROCRET = 12, WH_KEYBOARD_LL = 13, WH_MOUSE_LL = 14}
關於HookType中的WH_XXXX數值,請參照WinUser.h中的聲明。
class _HookProc{ #region "Declare API for Hook" [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern int SetWindowsHookEx(int idHook, _HookProcHandler lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern bool UnhookWindowsHookEx(int idHook); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] static extern int GetCurrentThreadId(); #endregion #region "Hook Proc" int MyHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if (m_CustomHookProc != null) m_CustomHookProc(nCode, wParam, lParam); return CallNextHookEx(m_HookHandle, nCode, wParam, lParam); } #endregion CustomHookProc.HookProcHandler m_CustomHookProc; delegate int _HookProcHandler(int nCode, IntPtr wParam, IntPtr lParam); _HookProcHandler m_KbdHookProc; int m_HookHandle = 0; public _HookProc(HookType a_eHookType, CustomHookProc.HookProcHandler a_pHookProc) { m_CustomHookProc = a_pHookProc; m_KbdHookProc = new _HookProcHandler(MyHookProc); m_HookHandle = SetWindowsHookEx((int)a_eHookType, m_KbdHookProc, IntPtr.Zero, GetCurrentThreadId()); if (m_HookHandle == 0) { throw new Exception(string.Format("Hook {0} to {1} Error:{2}", a_eHookType.ToString(), a_pHookProc.ToString(), Marshal.GetLastWin32Error())); } } ~_HookProc() { UnhookWindowsHookEx(m_HookHandle); Debug.WriteLine(Marshal.GetLastWin32Error()); m_HookHandle = 0; }}
public class CustomHookProc{ private CustomHookProc(){} public delegate void HookProcHandler(int nCode, IntPtr wParam, IntPtr lParam);}
前面的幾個API的聲明,是在.NET中呼叫Win32 API的方法(參考:http://msdn.microsoft.com/en-us/library/aa984739%28v=VS.71%29.aspx)。
重點是在MyHookProc,SetWindowsHookEx會將Hook指到這個函數,同時在這個函數執行時,會將Hook到的訊息丟到外部的CustomHookProc.HookProcHandler Delegate。
public class HookManager{ private HookManager(){} static readonly HookManager m_instance = new HookManager(); Dictionary<HookType, _HookProc> m_hooks = new Dictionary<HookType, _HookProc>(); public static HookManager Instance { get { return m_instance; } } public void RegisterHook(HookType a_eHookType, CustomHookProc.HookProcHandler a_pHookProc) { if(!m_hooks.ContainsKey(a_eHookType)) { m_hooks.Add(a_eHookType, new _HookProc(a_eHookType, a_pHookProc)); } else { throw new Exception(string.Format("{0} already exist!", a_eHookType.ToString())); } } public void Unregister(HookType a_eHookType) { m_hooks.Remove(a_eHookType); }}
HookManager類是一個單例類,它負責維護所有的Hook。
private void Form1_Load(object sender, EventArgs e){ HookManager.Instance.RegisterHook(HookType.WH_KEYBOARD, new CustomHookProc.HookProcHandler(KeyboardHookProc)); HookManager.Instance.RegisterHook(HookType.WH_MOUSE, new CustomHookProc.HookProcHandler(MouseHookProc));}void KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam){ KeyStateInfo ctrlKey = KeyboardInfo.GetKeyState(Keys.ControlKey); KeyStateInfo altKey = KeyboardInfo.GetKeyState(Keys.Alt); KeyStateInfo shiftKey = KeyboardInfo.GetKeyState(Keys.ShiftKey); KeyStateInfo f8Key = KeyboardInfo.GetKeyState(Keys.F8); if (ctrlKey.IsPressed) { Console.WriteLine("Ctrl Pressed!"); } if (altKey.IsPressed) { Console.WriteLine("Alt Pressed!"); } if (shiftKey.IsPressed) { Console.WriteLine("Shift Pressed!"); } if (f8Key.IsPressed) { Console.WriteLine("F8 Pressed!"); }}void MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam){ MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); if (nCode >= 0) { String strCaption = "x = " + MyMouseHookStruct.pt.x.ToString("d") + " y = " + MyMouseHookStruct.pt.y.ToString("d"); Form tempForm = Form.ActiveForm; tempForm.Text = strCaption; }}
封裝完畢後,使用起來就非常簡單
HookManager.Instance.RegisterHook(HookType.WH_KEYBOARD, new CustomHookProc.HookProcHandler(KeyboardHookProc));
HookManager.Instance.RegisterHook(HookType.WH_MOUSE, new CustomHookProc.HookProcHandler(MouseHookProc));
像這樣使用,而且在HookProc中也不需要去管return CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
只需針對自己所要處理的Hook處理即可。
參考連結
http://msdn.microsoft.com/zh-cn/magazine/cc188966(en-us).aspx
範例檔下載