简介
.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