很多人都知道,如果想在系統範圍內屏蔽鍵盤上的任意按鍵需要使用全局鍵盤鉤子,然而像win鍵這樣“倔強”的按鍵又不是普通的鍵盤鉤子就能搞定的。這裏我提供一種利用底層鍵盤鉤子屏蔽任意按鍵(包括win鍵)的方法,並且作成了.dll動態鏈接庫,方便以後使用。鉤子,是一種相對複雜一點的技術,通常用來監視系統中某一類型的事件,這些事件可以與某一線程相關(線程鉤子),也可以是系統中的所有線程(全局鉤子)。關於鉤子的理論,我不想說太多,也無法說太多,因爲那不是三言兩語就能說清楚的。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
本文的重點在於底層鍵盤鉤子的應用,前些天CSDN的VB版有人問如何實現屏蔽win鍵,說實話,這東西用VB也是可以做到的,只不過全局鉤子的鉤子函數必須寫在標準dll中,而VB只能通過變通的方法做出標準dll,稍微有點麻煩,所以我索性用VC寫了一個dll,這樣VC、VB或Delphi等等都可以調用,而且我也留出了足夠的接口,稍後就會看到。
有一點必須得聲明一下,底層鍵盤鉤子有一個半致命的缺點,就是只能在NT及其以上系統中使用,不過好在現在用2000、XP、2003的人絕對不在少數,將來用LongHorn的人估計也少不了,所以這點倒是不用太擔心。 :)
好了,閒話少說,源代碼在此:
DLL頭文件(在VC中使用這個DLL中的函數時,需要包含這個頭文件,就像使用API要包含windows.h一樣):
/********************************************************************/ /* 文件名: MaskKey.h */ /* */ /* 功能: 標準 DLL 導出函數頭文件, 在使用該DLL的程序中包含此文件 */ /* */ /* 作者: 盧培培 (goodname008) 時間: 2004.8.21 */ /* */ /* BLOG: http://blog.csdn.net/goodname008 */ /********************************************************************/
DECLSPEC_IMPORT BOOL WINAPI StartMaskKey( LPDWORD lpdwVirtualKey, int nLength, BOOL bDisableKeyboard = FALSE );
DECLSPEC_IMPORT BOOL WINAPI StopMaskKey(); |
DLL主文件:
/********************************************************************/ /* 文件名: MaskKey.cpp */ /* */ /* 功能: 標準 DLL ---- 利用底層鍵盤鉤子實現屏蔽鍵盤任意按鍵 */ /* */ /* 作者: 盧培培 (goodname008) 時間: 2004.8.21 */ /* */ /* BLOG: http://blog.csdn.net/goodname008 */ /********************************************************************/
// 導出函數列表 // StartMaskKey // StopMaskKey
#define _WIN32_WINNT 0x0500 // 設置系統版本, 確保可以使用底層鍵盤鉤子
#include "windows.h"
// 全局變量 LPDWORD g_lpdwVirtualKey = NULL; // Keycode 數組的指針 int g_nLength = 0; // Keycode 數組的大小 BOOL g_bDisableKeyboard = FALSE; // 是否屏蔽整個鍵盤 HINSTANCE g_hInstance = NULL; // 模塊實例句柄 HHOOK g_hHook = NULL; // 鉤子句柄
// DLL 入口函數 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { // 保存模塊實例句柄 g_hInstance = (HINSTANCE)hModule;
// 在進程結束或線程結束時卸載鉤子 switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: break; case DLL_THREAD_ATTACH: break; case DLL_PROCESS_DETACH: case DLL_THREAD_DETACH: delete g_lpdwVirtualKey; if (g_hHook != NULL) UnhookWindowsHookEx(g_hHook); break; } return TRUE; }
// 底層鍵盤鉤子函數 LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { // 禁用鍵盤的某個按鍵, 如果 g_bDisableKeyboard 爲 TRUE 則禁用整個鍵盤 if (nCode == HC_ACTION) { if (g_bDisableKeyboard) return TRUE; KBDLLHOOKSTRUCT* pStruct = (KBDLLHOOKSTRUCT*)lParam; LPDWORD tmpVirtualKey = g_lpdwVirtualKey; for (int i = 0; i < g_nLength; i++) { if (pStruct->vkCode == *tmpVirtualKey++) return TRUE; }
}
// 傳給系統中的下一個鉤子 return CallNextHookEx(g_hHook, nCode, wParam, lParam); }
/********************************************************************/ /* 開始屏蔽鍵盤按鍵 */ /* */ /* 參數: */ /* lpdwVirtualKey Keycode 數組的指針 */ /* nLength Keycode 數組的大小 */ /* bDisableKeyboard 是否屏蔽整個鍵盤 */ /* */ /* 返回值: TRUE 成功, FALSE 失敗 */ /********************************************************************/ BOOL WINAPI StartMaskKey(LPDWORD lpdwVirtualKey, int nLength, BOOL bDisableKeyboard = FALSE) { // 如果已經安裝鍵盤鉤子則返回 FALSE if (g_hHook != NULL) return FALSE;
// 將用戶傳來的 keycode 數組保存在全局變量中 g_lpdwVirtualKey = (LPDWORD)malloc(sizeof(DWORD) * nLength); LPDWORD tmpVirtualKey = g_lpdwVirtualKey; for (int i = 0; i < nLength; i++) { *tmpVirtualKey++ = *lpdwVirtualKey++; } g_nLength = nLength; g_bDisableKeyboard = bDisableKeyboard;
// 安裝底層鍵盤鉤子 g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, g_hInstance, NULL); if (g_hHook == NULL) return FALSE; return TRUE;
}
/********************************************************************/ /* 停止屏蔽鍵盤按鍵 */ /* */ /* 參數: (無) */ /* */ /* 返回值: TRUE 成功, FALSE 失敗 */ /********************************************************************/ BOOL WINAPI StopMaskKey() { // 卸載鉤子 if (UnhookWindowsHookEx(g_hHook) == 0) return FALSE; g_hHook = NULL; return TRUE; } |
DEF文件(MaskKey.def):
EXPORTS StartMaskKey @1 StopMaskKey @2 |
上面就是DLL工程中主要的三個文件,工程類型爲Win32 Dynamic-Link Library。從DEF文件可以看出,DLL共有兩個導出函數:StartMaskKey和StopMaskKey。
StartMaskKey有三個參數,lpdwVirtualKey是一個指向DWORD數組的指針,該DWORD數組用來存放virtual-key code,nLength是該數組的大小,bDisableKeyboard是個邏輯值,如果爲TRUE表示禁用整個鍵盤,默認爲FALSE。使用正確的參數調用StartMaskKey,DLL可以將DWORD數組中每一個virtual-key code與鍵盤上對應的按鍵屏蔽掉,按這些鍵將完全沒有反應(包括win鍵)。事實上,對於virtual-key code只要一個字節就可以表示了,但KBDLLHOOKSTRUCT結構中的vkCode是DWORD型,所以爲求統一我也採用4個字節(DWORD)。縱然如此,微軟還是在MSDN中強調了,virtual-key code的值必須是1到254之間的值,這點一定要注意。
StopMaskKey沒有參數,表示停止屏蔽鍵盤按鍵。如果在程序中沒有調用StopMaskKey停止屏蔽鍵盤按鍵,在進程或線程退出時將自動停止屏蔽,恢復原來的狀態。當然進程和線程一定要正常退出,如果是被別的程序以TerminateProcess或TerminateThread等微軟不太建議使用的野蠻手段結束進程或線程的話,就不太好辦了。 :(
下面是在VC中調用的例子:(兩個Dialog的成員函數,對應兩個按鈕)
void CMaskKeyAppDlg::OnStartmaskkey() { // 屏蔽 A, B, C, 上, 下, 左, 右及兩個win鍵 DWORD dwVK[] = {'A', 'B', 'C', VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_LWIN, VK_RWIN}; int nLength = sizeof(dwVK) / sizeof(DWORD); StartMaskKey(dwVK, nLength);
}
void CMaskKeyAppDlg::OnStopmaskkey() { StopMaskKey();
} |
下面是在VB中調用的例子:(在窗體上添加2個CommandButton,並分別改名爲cmdStartMask和cmdStopMask)
Option Explicit Private Declare Function StartMaskKey Lib "MaskKey" (lpdwVirtualKey As Long, ByVal nLength As Long, Optional ByVal bDisableKeyboard As Boolean = False) As Long Private Declare Function StopMaskKey Lib "MaskKey" () As Long
Private Sub cmdStartMask_Click() ' 屏蔽 A, B, C, 上, 下, 左, 右及兩個win鍵 Dim key(8) As Long key(0) = vbKeyA key(1) = vbKeyB key(2) = vbKeyC key(3) = vbKeyLeft key(4) = vbKeyRight key(5) = vbKeyUp key(6) = vbKeyDown key(7) = &H5B ' 左邊的win鍵 key(8) = &H<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />5C ' 右邊的win鍵 StartMaskKey key(0), UBound(key) + 1 End Sub
Private Sub cmdStopMask_Click() StopMaskKey End Sub |
對於VB,如果是在VB的IDE環境中按F5啓動程序,則必須調用StopMaskKey才能使鍵盤恢復狀態,如果沒有調用,則在退出VB的IDE環境時由DLL恢復鍵盤狀態。對於編譯後獨立執行的VB程序,則和VC編譯後的程序一樣,無論是否調用StopMaskKey,都將在程序退出時由DLL自動卸載鉤子,恢復鍵盤狀態。
其實,鉤子並不是什麼很深奧的技術,我寫這個DLL的目的,也是爲了我們在以後用到的時候,可以實行“拿來主義”。
DLL源代碼及VC和VB調用例程的下載地址:http://csdngoodname008.51.net/MaskKey.zip
*-------------------------------------------*
* 轉載請通知作者並註明出處,CSDN歡迎您! *
* 作者:盧培培(goodname008) *
* 郵箱:[email protected] *
* 專欄:http://blog.csdn.net/goodname008 *
*-------------------------------------------*