今天,有个同事问我,怎样在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...”,这可怎么办呢?class="Apple-converted-space"> 别担心,办法总是有的,经过一番摸索以后,发现 WH_KEY BORA D_LL和WH_MOUSE_LL这两个low-level的hook可以被安装成全局的,这就好办了,我们不妨用这两个low-level的hook替换掉WH_KEYBORAD和WH_MOUSE,于是开始测试。结果成功了,在C#里实现了全局钩子。 我们来看一下主要代码段。 首先倒入所需要的windows函数,主要有三个,SetWindowsHookEX用来安装钩子,UnhookWindowsHookEX用来卸载 钩子以及CallNextHookEX用来将hook信息传递到链表中下一个hook处理过程。
C#代码
javascripts/syntaxhighlighter/clipboard_new.swf" type="application/x-shockwave-flash"> break%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20WM_RBUTTONDOWN%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fcase%20WM_RBUTTONUP%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fcase%20WM_RBUTTONDBLCLK%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20button%20%3D%20MouseButtons.Right%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20WM_MOUSEWHEEL%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FIf%20the%20message%20is%20WM_MOUSEWHEEL%2C%20the%20high-order%20word%20of%20mouseData%20member%20is%20the%20wheel%20delta.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FOne%20wheel%20click%20is%20defined%20as%20WHEEL_DELTA%2C%20which%20is%20120.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F(value%20%3E%3E%2016)%20%26%200xffff%3B%20retrieves%20the%20high-order%20word%20from%20the%20given%2032-bit%20value%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mouseDelta%20%3D%20(short)((mouseHookStruct.mouseData%20%3E%3E%2016)%20%26%200xffff)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FTODO%3A%20X%20BUTTONS%20(I%20havent%20them%20so%20was%20unable%20to%20test)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FIf%20the%20message%20is%20WM_XBUTTONDOWN%2C%20WM_XBUTTONUP%2C%20WM_XBUTTONDBLCLK%2C%20WM_NCXBUTTONDOWN%2C%20WM_NCXBUTTONUP%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2For%20WM_NCXBUTTONDBLCLK%2C%20the%20high-order%20word%20specifies%20which%20X%20button%20was%20pressed%20or%20released%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fand%20the%20low-order%20word%20is%20reserved.%20This%20value%20can %20be%20one%20or%20more%20of%20the%20following%20values.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FOtherwise%2C%20mouseData%20is%20not%20used.%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fdouble%20clicks%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20clickCount%20%3D%200%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(button%20!%3D%20MouseButtons.None)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(wParam%20%3D%3D%20WM_LBUTTONDBLCLK%20%7C%7C%20wParam%20%3D%3D%20WM_RBUTTONDBLCLK)%20clickCount%20%3D%202%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20clickCount%20%3D%201%3B%0A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fgenerate%20event%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20MouseEventArgs%20e%20%3D%20new%20MouseEventArgs(%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20button%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20clickCount%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mouseHookStruct.pt.x%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mouseHookStruct.pt.y%2C%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mouseDelta)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fraise%20it%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20OnMouseActivity(this%2C%20e)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fcall%20next%20hook%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20CallNextHookEx(hMouseHook%2C%20nCode%2C%20wParam%2C%20lParam)%3B%0A%0A%20%20%20%20%20%20%20%20%7D%0A%0A%0Aprivate%20int%20KeyboardHookProc(int%20nCode%2C%20Int32%20wParam%2C%20IntPtr%20lParam)%0A%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Findicates%20if%20any%20of%20underlaing%20events%20set%20e.Handled%20flag%0A%20%20%20%20%20%20%20%20%20%20%20%20bool%20handled%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fit%20was%20ok%20and%20someone%20listens%20to%20events%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20((nCode%20%3E%3D%200)%20%26%26%20(KeyDown%20!%3D%20null%20%7C%7C%20KeyUp%20!%3D%20null%20%7C%7C%20KeyPress%20!%3D%20null))%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fread%20structure%20KeyboardHookStruct%20at%20lParam%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyboardHookStruct%20MyKeyboardHookStruct%20%3D%20(KeyboardHookStruct)Marshal.PtrToStructure(lParam%2C%20typeof(KeyboardHookStruct))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fraise%20KeyDown%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(KeyDown%20!%3D%20null%20%26%26%20(wParam%20%3D%3D%20WM_KEYDOWN%20%7C%7C%20wParam%20%3D%3D%20WM_SYSKEYDOWN))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Keys%20keyData%20%3D%20(Keys)MyKeyboardHookStruct.vkCode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyEventArgs%20e%20%3D%20new%20KeyEventArgs(keyData)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyDown(this%2C%20e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handled%20%3D%20handled%20%7C%7C%20e.Handled%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20raise%20KeyPress%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(KeyPress%20!%3D%20null%20%26%26%20wParam%20%3D%3D%20WM_KEYDOWN)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bool%20isDownShift%20%3D%20((GetKeyState(VK_SHIFT)%20%26%200x80)%20%3D%3D%200x80%20%3F%20true%20%3A%20false)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bool%20isDownCapslock%20%3D%20(GetKeyState(VK_CAPITAL)%20!%3D%200%20%3F%20true%20%3A%20false)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20byte%5B%5D%20keyState%20%3D%20new%20byte%5B256%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20GetKeyboardState(keyState)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20byte%5B%5D%20inBuffer%20%3D%20new%20byte%5B2%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(ToAscii(MyKeyboardHookStruct.vkCode%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20MyKeyboardHookStruct.scanCode%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20keyState%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inBuffer%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20MyKeyboardHookStruct.flags)%20%3D%3D%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20char%20key%20%3D%20(char)inBuffer%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20((isDownCapslock%20%5E%20isDownShift)%20%26%26%20Char.IsLetter(key))%20key%20%3D%20Char.ToUpper(key)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyPressEventArgs%20e%20%3D%20new%20KeyPressEventArgs(key)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyPress(this%2C%20e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handled%20%3D%20handled%20%7C%7C%20e.Handled%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20raise%20KeyUp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(KeyUp%20!%3D%20null%20%26%26%20(wParam%20%3D%3D%20WM_KEYUP%20%7C%7C%20wParam%20%3D%3D%20WM_SYSKEYUP))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Keys%20keyData%20%3D%20(Keys)MyKeyboardHookStruct.vkCode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyEventArgs%20e%20%3D%20new%20KeyEventArgs(keyData)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20KeyUp(this%2C%20e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handled%20%3D%20handled%20%7C%7C%20e.Handled%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fif%20event%20handled%20in%20application%20do%20not%20handoff%20to%20other%20listeners%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(handled)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20CallNextHookEx(hKeyboardHook%2C%20nCode%2C%20wParam%2C%20lParam)%3B%0A%20%20%20%20%20%20%20%20%7D" /> ways" />
[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);
下面是有关这两个low-level hook在Winuser.h中的定义:
private const int WH_MOUSE_LL = 14;
private const int WH_KEYBOARD_LL = 13;
在安装全局钩子的时候,我们就要做替换了,将WH_MOUSE和WH_KEYBORAD分别换成WH_MOUSE_LL和WH_KEYBORAD_LL:
hMouseHook = SetWindowsHookEx(
WH_MOUSE_LL,
MouseHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
hKeyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
这样替换了之后,我们就可以实现全局钩子了,而且,不需要写DLL。看一下程序运行情况:
下面是关于鼠标和键盘的两个Callback函数:
private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
if ((nCode >= 0) && (OnMouseActivity != null))
{
MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
MouseButtons button = MouseButtons.None;
short mouseDelta = 0;
switch (wParam)
{
case WM_LBUTTONDOWN:
button = MouseButtons.Left;
break;
case WM_RBUTTONDOWN:
button = MouseButtons.Right;
break;
case WM_MOUSEWHEEL:
mouseDelta = (short)((mouseHookStruct.mouseData >> 16) & 0xffff);
break;
}
int clickCount = 0;
if (button != MouseButtons.None)
if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK) clickCount = 2;
else clickCount = 1;
MouseEventArgs e = new MouseEventArgs(
button,
clickCount,
mouseHookStruct.pt.x,
mouseHookStruct.pt.y,
mouseDelta);
OnMouseActivity(this, e);
}
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
bool handled = false;
if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
handled = handled || e.Handled;
}
if (KeyPress != null && wParam == WM_KEYDOWN)
{
bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (ToAscii(MyKeyboardHookStruct.vkCode,
MyKeyboardHookStruct.scanCode,
keyState,
inBuffer,
MyKeyboardHookStruct.flags) == 1)
{
char key = (char)inBuffer[0];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
KeyPress(this, e);
handled = handled || e.Handled;
}
}
if (KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUp(this, e);
handled = handled || e.Handled;
}
}
if (handled)
return 1;
else
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}