在實際應用中,鍵盤監控是一種很常見的技術,它包括按鍵的記錄、按鍵的過濾、按鍵的修改(映射)等。比方說,我們想統計用戶的擊鍵情況,這個就是按鍵的記錄;我們想屏蔽某些系統鍵(例如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)