今天,有个同事问我,怎样在C#中使用全局钩子?以前写的全局钩子都是用unmanaged C或C++写个DLL来实现,可大家都知道,C#是基于.Net Framework的,是managed,怎么实现全局钩子呢?于是开始到网上搜索,好不容易找到一篇,318804 - HOW TO: Set a Windows Hook in Visual C# .NET,里面详细的说明了如何使用鼠标钩子捕获鼠标的移动等,可是,它只能在Application里起作用,出了Application就没用了,就是说它还是没有实现全局钩子,而且文章结尾处说:“Global Hooks are not supported in the .NET Framework...”,这可怎么办呢?
别担心,办法总是有的,经过一番摸索以后,发现WH_KEYBORAD_LL和WH_MOUSE_LL这两个low-level的hook可以被安装成全局的,这就好办了,我们不妨用这两个low-level的hook替换掉WH_KEYBORAD和WH_MOUSE,于是开始测试。结果成功了,在C#里实现了全局钩子。
我们来看一下主要代码段。
首先倒入所需要的windows函数,主要有三个,SetWindowsHookEX用来安装钩子,UnhookWindowsHookEX用来卸载钩子以及CallNextHookEX用来将hook信息传递到链表中下一个hook处理过程。
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);
private const int WH_MOUSE_LL = 14;
private const int WH_KEYBOARD_LL = 13;
//install hook
hMouseHook = SetWindowsHookEx(
WH_MOUSE_LL, //原来是WH_MOUSE
MouseHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
//install hook
hKeyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL, //原来是WH_KEYBORAD
KeyboardHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
|
private int MouseHookProc(int nCode, int wParam, IntPtr lParam) { // if ok and someone listens to our events if ((nCode >= 0) && (OnMouseActivity != null)) { //Marshall the data from callback. MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct)); //detect button clicked MouseButtons button = MouseButtons.None; short mouseDelta = 0; switch (wParam) { case WM_LBUTTONDOWN: //case WM_LBUTTONUP: //case WM_LBUTTONDBLCLK: button = MouseButtons.Left; break; case WM_RBUTTONDOWN: //case WM_RBUTTONUP: //case WM_RBUTTONDBLCLK: button = MouseButtons.Right; break; case WM_MOUSEWHEEL: //If the message is WM_MOUSEWHEEL, the high-order word of mouseData member is the wheel delta. //One wheel click is defined as WHEEL_DELTA, which is 120. //(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value mouseDelta = (short)((mouseHookStruct.mouseData >> 16) & 0xffff); //TODO: X BUTTONS (I havent them so was unable to test) //If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, //or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released, //and the low-order word is reserved. This value can be one or more of the following values. //Otherwise, mouseData is not used. break; } //double clicks int clickCount = 0; if (button != MouseButtons.None) if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK) clickCount = 2; else clickCount = 1; //generate event MouseEventArgs e = new MouseEventArgs( button, clickCount, mouseHookStruct.pt.x, mouseHookStruct.pt.y, mouseDelta); //raise it OnMouseActivity(this, e); } //call next hook return CallNextHookEx(hMouseHook, nCode, wParam, lParam); } |
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)