您的位置:首页 > 编程语言 > C#

在C#中使用全局鼠标、键盘Hook

2014-07-04 13:50 393 查看
今天,有个同事问我,怎样在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处理过程。

C#代码



[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中的定义:

/// <summary>

/// Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events.

/// </summary>

private const int WH_MOUSE_LL = 14;

/// <summary>

/// Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard input events.

/// </summary>

private const int WH_KEYBOARD_LL = 13;

  在安装全局钩子的时候,我们就要做替换了,将WH_MOUSE和WH_KEYBORAD分别换成WH_MOUSE_LL和WH_KEYBORAD_LL:

//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);

  这样替换了之后,我们就可以实现全局钩子了,而且,不需要写DLL。看一下程序运行情况:

  下面是关于鼠标和键盘的两个Callback函数:

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)

{

//indicates if any of underlaing events set e.Handled flag

bool handled = false;

//it was ok and someone listens to events

if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))

{

//read structure KeyboardHookStruct at lParam

KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

//raise KeyDown

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;

}

// raise KeyPress

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;

}

}

// raise KeyUp

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 event handled in application do not handoff to other listeners

if (handled)

return 1;

else

return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: