在实际应用中,键盘监控是一种很常见的技术,它包括按键的记录、按键的过滤、按键的修改(映射)等。比方说,我们想统计用户的击键情况,这个就是按键的记录;我们想屏蔽某些系统键(例如Alt键、Win键),这个是按键的过滤;我们想改变按键的值,例如按下A,出来的是Z,在例如按下A,出来按键的组合SDFG等(貌似这个在游戏中比较多,有些游戏的大绝招都比较难按,用这个一劳永逸),这个是按键的修改。
键盘监控的具体实现,用的是微软的Keyboard Hook API函数。
首先解释下,什么是Hook函数。
WINDOW的消息处理机制为了能在应用程序中监控系统的各种事件消息,提供了挂接各种反调函数(HOOK)的功能。这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一个挂接函数链。系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。WINDOW系统的这种反调函数挂接方法虽然会略加影响到系统的运行效率,但在很多场合下是非常有用的,通过合理有效地利用键盘事件的挂钩函数监控机制可以达到预想不到的良好效果。
简单的说,就是在消息到达Window之前,系统允许你安装Hook函数来拦截消息,并对消息进行处理。Hook函数也是有类别的,不同的函数实现不同的功能。
先看下函数的申明:
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
ByVal idHook As Integer, _ '安装的钩子的类型
ByVal lpfn As HookProc, _ '消息的处理函数
ByVal hMod As IntPtr, _ '应用程序事例句柄
ByVal dwThreadId As Integer _ '线程ID
) As Integer
钩子卸载函数
Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
ByVal idHook As Integer _ '要卸载的HOOK函数的句柄
) As Integer
调用下一个HOOK函数
Private Declare Function CallNextHookEx Lib "user32" ( _
ByVal idHook As Integer, _ '本HOOK函数的句柄
ByVal nCode As Integer, _ '消息的类型
ByVal wParam As Integer, _ '消息的参数
ByVal lParam As IntPtr _ '消息的参数
) As Integer
消息函数的委托
Private Delegate Function HookProc( _
ByVal nCode As Integer, _ '消息的类型
ByVal wParam As Integer, _ '消息的参数
ByVal lParam As IntPtr _ '消息的参数
) As Integer
钩子类型的常数
Private Const WH_KEYBOARD_LL As Integer= 13 '全局键盘钩子(又称为底层)
Private Const WH_KEYBOARD As Integer = 2 '普通键盘钩子
按键信息结构
Public Structure KeyboardHookStruct
Dim vkCode As Integer
Dim ScanCode As Integer
Dim Flags As Integer
Dim Time As Integer
Dim DwExtraInfo As Integer
End Structure
我们用一个类来实现键盘的监控。
首先定义两个变量
Private hKeyboardHook As Integer
Private KeyboardHookProcedure As HookProc
装载钩子的函数
Public Sub Hook()
If hKeyboardHook = 0 Then
KeyboardHookProcedure = New HookProc(AddressOf KeyboardHookProc)
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly.GetModules()(0)), 0)
If hKeyboardHook = 0 Then
UnHook()
Throw New Win32Exception(Marshal.GetLastWin32Error)
End If
End If
End Sub
注:函数执行后,会安装Hook,所有的按键消息在到达window前都会被函数KeyboardHookProc拦截到。我们在后面的KeyboardHookProc函数中处理拦截的消息。
卸载钩子的函数
Public Sub UnHook()
If hKeyboardHook <> 0 Then
Dim retKeyboard As Integer = UnhookWindowsHookEx(hKeyboardHook)
hKeyboardHook = 0
If retKeyboard = 0 Then Throw New Win32Exception(Marshal.GetLastWin32Error)
End If
End Sub
按键消息的处理函数
Private Function KeyboardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)
自己处理的一些代码,例如:记录、屏蔽、映射等
Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
End Function
以上就是基本的按键监控类的代码。不过要注意以下几点:
1、Keyboard的HOOK函数分为两种,WH_KEYBOARD_LL和WH_KEYBOARD。我们一般用第一种,全局的键盘钩子,能拦截所有的键盘按键的消息。
2、网上有人说,全局的钩子要放在单独的DLL中才能使用。我试了一下,不放在单独的DLL中,在XP+VS2005下,调试和运行都没有问题;在XP+VS2008下,调试会出错,不过编译后能运行;在WIN7+VS2010下,调试会出错,编译后能运行。这方面有研究的网友,望不吝赐教。
3、WH_KEYBOARD_LL和WH_KEYBOARD,这是两种不同的钩子,虽然最后都是KeyboardHookProc函数处理拦截的消息,但是具体的每个参数的意义却完全不一样。
WH_KEYBOARD钩子。KeyboardHookProc函数的各个参数意义如下:
nCode 消息的类型,分HC_ACTION和HC_NOREMOVE
wParam 按键的虚拟键码
lParam 按键的相关参数信息,包括重复时间、按键的状态(按下或弹起)等
WH_KEYBOARD_LL钩子。KeyboardHookProc函数的各个参数意义如下:
nCode 消息的类型,有HC_ACTION
wParam 按键的状态(按下或弹起)WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP
lParam 指向KeyboardHookStruct结构的指针,该结构包含了按键的详细信息。
可以看出,这两种钩子的参数的定义是完全不一样的。而在之前的代码中:Dim MyKeyboardHookStruct As KeyboardHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(KeyboardHookStruct)), KeyboardHookStruct)就是将该指针指向的内容复制到指定的结构中。
本文介绍了全局键盘钩子的实现中的一些基本的HOOK API函数的介绍。具体的实现留待后文详解。