在上文“鍵盤監控的實現Ⅰ——Keyboard Hook API函數”中介紹了鍵盤的Hook API函數。
重點就在按鍵消息處理函數
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
先看看CallNextHookEx函數,從字面的理解就是調用後面一個鉤子函數。若後面已經沒有鉤子函數呢?很多人都會錯誤的認為將會將消息傳遞給Window的消息處理函數。他們認為,消息的處理流程如下面所示:假設有4個鉤子函數,分別為鉤子A、鉤子B、鉤子C、鉤子D
物理擊鍵
↓
鉤子A
↓
鉤子B
↓
鉤子C
↓
鉤子D
↓
Window的消息處理函數
他們認為,四個鉤子函數中只要有一個返回1(非0),將會中止消息的傳遞。甚至在鉤子函數中不調用CallNextHookEx函數也會阻止消息的傳遞。甚至認為,修改CallNextHookEx函數的參數就能更改按鍵消息的傳遞。
遺憾的是,這個思路是不對的。
你可以在鉤子函數中刪除CallNextHookEx函數的調用,會發現Window還是得到了按鍵的消息。你也可以嘗試修改CallNextHookEx函數的參數,看看會有什麼效果。我這樣嘗試後,直接報錯(甚至有莫名的退出)。
再回過頭來看看CallNextHookEx函數,發現它僅僅是調用下一個鉤子函數,只是在鉤子函數間傳遞信息。
正確的消息處理流程應該如下:還是以上面的事例為例。
物理擊鍵
↓
鉤子管理函數←→鉤子A←→鉤子B←→鉤子C←→鉤子D
↓
Window消息處理函數
在鉤子A函數中,如果調用CallNextHookEx函數,則會將按鍵消息傳給鉤子B;如果不調用CallNextHookEx函數,則鉤子B不會得到按鍵消息,換句話說,鉤子B失效了,當然此時的鉤子C和鉤子D也失效了。為了鉤子間和平相處,還是應該在鉤子函數裡添加CallNextHookEx函數的調用。
再說說鉤子函數的返回值的問題。在上面的事例中,鉤子A的返回值決定按鍵消息是否丟棄。返回值0,告訴系統,消息繼續傳遞給Window消息處理函數;返回值1(非0),告訴系統,消息將丟棄,Window消息處理函數得不到按鍵的消息。
所以說,如果只是統計按鍵的信息
在鉤子函數中的最後直接調用
Return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
由後面的鉤子函數來決定是否丟棄該消息。(大家和平相處)
如果是屏蔽按鍵
在鉤子函數中進行判斷,滿足要求後直接
CallNextHookEx(hKeyboardHook, nCode, wParam, lParam)
Return 1
告訴系統,丟棄該消息。當然出於禮貌,在之前還是調用CallNextHookEx函數,以便其他的鉤子函數處理該消息
至於修改按鍵(映射按鍵),修改參數,調用CallNextHookEx函數是沒有用的。因為原本的消息根本就沒有修改,你改的只是傳給其他鉤子函數的消息。而且還非常容易出錯。
關於如何修改按鍵,將在後文介紹。