WINDOW的消息處理機制為了能在應用程序中監控系統的各種事件消息,提供了掛接各種反調函數(HOOK)的功能。這種掛鉤函數(HOOK)類似擴充中斷驅動程序,掛鉤上可以掛接多個反調函數構成一個掛接函數鏈。系統產生的各種消息首先被送到各種掛接函數,掛接函數根據各自的功能對消息進行監視、修改和控制等,然後交還控制權或將消息傳遞給下一個掛接函數以致最終達到窗口函數。WINDOW系統的這種反調函數掛接方法雖然會略加影響到系統的運行效率,但在很多場合下是非常有用的,通過合理有效地利用鍵盤事件的掛鉤函數監控機制可以達到預想不到的良好效果。
一、在WINDOWS鍵盤事件上掛接監控函數的方法
WINDOW下可進行掛接的過濾函數包括11種:
WH_CALLWNDPROC 窗口函數的過濾函數
WH_CBT 計算機培訓過濾函數
WH_DEBUG 調試過濾函數
WH_GETMESSAGE 獲取消息過濾函數
WH_HARDWARE 硬件消息過濾函數
WH_JOURNALPLAYBACK 消息重放過濾函數
WH_JOURNALRECORD 消息記錄過濾函數
WH_MOUSE 鼠標過濾函數
WH_MSGFILTER 消息過濾函數
WH_SYSMSGFILTER 系統消息過濾函數
WH_KEYBOARD 鍵盤過濾函數
其中鍵盤過濾函數是最常用最有用的過濾函數類型,不管是哪一種類型的過濾函數,其掛接的基本方法都是相同的。
WINDOW調用掛接的反調函數時總是先調用掛接鏈首的那個函數,因此必須將鍵盤掛鉤函數利用函數SetWindowsHookEx()將其掛接在函數鏈首。至於消息是否傳遞給函數鏈的下一個函數是由每個具體函數功能確定的,如果消息需要傳統給下一個函數,可調用API函數的CallNextHookEx()來實現,如果不傳遞直接返回即可。掛接函數可以是用來監控所有線程消息的全局性函數,也可以是單獨監控某一線程的局部性函數。如果掛接函數是局部函數,可以將它放到一個.DLL動態鏈接庫中,也可以放在一個局部模塊中;如果掛接函數是全局的,那麼必須將其放在一個.DLL動態鏈接庫中。掛接函數必須嚴格按照下述格式進行聲明,以鍵盤掛鉤函數為例:
int FAR PASCAL KeyboardProc(int nCode,WORD wParam,DWORD lParam)
其中KeyboardProc為定義掛接函數名,該函數必須在模塊定義文件中利用EXPORTS命令進行說明;nCode決定掛接函數是否對當前消息進行處理;wParam和lParam為具體的消息內容。
二、鍵盤事件掛接函數的安裝與下載
在程序中可以利用函數SetWindowsHookEx()來掛接過濾函數,在掛接函數時必須指出該掛接函數的類型、函數的入口地址以及函數是全局性的還是局部性的,掛接函數的具體調用格式如下:
SetWindowsHookEx(iType,iProc,hInst,iCode)
其中iType為掛接函數類型,鍵盤類型為WH_KEYBOARD,iProc為掛接函數地址,hInst為掛接函數鏈接庫實例句柄,iCode為監控代碼-0表示全局性函數。如果掛接函數需要將消息傳遞給下一個過濾函數,則在該掛接函數返回前還需要調用一次CallNextHookEx()函數,當需要下載掛接函數時,只要調用一次UnhookWindowsHookEx(iProc)函數即可實現。如果函數是全局性的,那麼它必須放在一個.DLL動態鏈接庫中,這時該函數調用方法可以和其它普通.DLL函數一樣有三種:
1.在DEF定義文件中直接用函數名或序號說明:
EXPORTS
WEP @1 RESIDENTNAME
InitHooksDll @2
InstallFilter @3
KeyboardProc @4
用序號說明格式為:鏈接庫名.函數名(如本例中說明方法為KEYDLL.KeyboardProc)。
2.在應用程序中利用函數直接調用:
首先在應用程序中利用LoadLibrary(LPSTR "鏈接庫名")將動態鏈接庫裝入,並取得裝載庫模塊句柄hInst,然後直接利用GetProcAddress(HINSTANCE hInst,LPSTR "函數過程名")獲取函數地址,然後直接調用該地址即可,程序結束前利用函數FreeLibrary( )釋放裝入的動態鏈接庫即可。
3.利用輸入庫.LIB方法
利用IMPLIB.EXE程序在建立動態鏈接庫的同時建立相應的輸入庫.LIB,然後直接在項目文件中增加該輸入庫。
三、WINDOWS掛鉤監控函數的實現步驟
WINDOWS掛鉤函數只有放在動態鏈接庫DLL中才能實現所有事件的監控功能。在.DLL中形成掛鉤監控函數基本方法及其基本結構如下:
1、首先聲明DLL中的變量和過程;
2、然後編制DLL主模塊LibMain(),建立模塊實例;
3、建立系統退出DLL機制WEP()函數;
4、完成DLL初始化函數InitHooksDll(),傳遞主窗口程序句柄;
5、編制掛鉤安裝和下載函數InstallFilter();
6、編制掛鉤函數KeyboardProc(),在其中設置監控功能,並確定繼續調下一個鉤
子函數還是直接返回WINDOWS應用程序。
7、在WINDOWS主程序中需要初始化DLL並安裝相應掛鉤函數,由掛接的鉤子函數負
責與主程序通信;
8、在不需要監控時由下載功能卸掉掛接函數。
四、WINDOWS下鍵盤掛鉤監控函數的應用技術
目前標准的104 鍵盤上都有兩個特殊的按鍵,其上分別用WINDOW程序徽標和鼠標下拉列表標識,本文暫且分別稱為Micro左鍵和Micro右鍵,前者用來模擬鼠標左鍵激活開始菜單,後者用來模擬鼠標右鍵激活屬性菜單。這兩個特殊按鍵只有在按下後立即抬起即完成 CLICK過程才能實現其功能,並且沒有和其它按鍵進行組合使用。由於WINDOWS 系統中將按鍵劃分得更加詳細,使應用程序中很難靈活定義自己的專用快捷鍵,比如在開發.IME等應用程序時很難找到不與WORD8.0等其它應用程序沖突的功能按鍵。如果將標准104鍵盤中的這兩個特殊按鍵作為模擬CTRL和ALT 等專用按鍵,使其和其它按鍵組合,就可以在自己的應用程序中自由地設置專用功能鍵,為應用程序實現各種功能快捷鍵提供靈活性。正常情況下WINDOWS 鍵盤事件驅動程序並不將這兩個按鍵的消息進行正常解釋,這就必須利用鍵盤事件的掛鉤監控函數來實現其特定的功能。其方法如下:
1、首先編制如下一個簡單動態鏈接庫程序,並編譯成DLL文件。
#include "windows.h"
int FAR PASCAL LibMain(HANDLE hModule,UINT wDataSeg,UINT cbHeapSize,LPSTR lpszCmdLine);
int WINAPI WEP(int bSystemExit);
int WINAPI InitHooksDll(HWND hwndMainWindow);
int WINAPI InstallFilter(BOOL nCode);
LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam);
static HANDLE hInstance; // 全局句柄
static HWND hWndMain; // 主窗口句柄
static int InitCalled=0; // 初始化標志
static HHOOK hKeyHook;
FARPROC lpfnKeyHook=(FARPROC)KeyHook;
BOOL HookStates=FALSE;
int FAR PASCAL LibMain(
HANDLE hModule,
UINT wDataSeg,
UINT cbHeapSize,
LPSTR lpszCmdLine)
{
if (cbHeapSize!=0) UnlockData(0);
hInstance = hModule;
return 1;
}
int WINAPI WEP (int bSystemExit)
{ return 1;}
int WINAPI InitHooksDll(HWND hwndMainWindow)
{ hWndMain = hwndMainWindow;
InitCalled = 1;
return (0);
}
int WINAPI InstallFilter(BOOL nCode)
{ if (InitCalled==0) return (-1);
if (nCode==TRUE) {
hKeyHook=SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)lpfnKeyHook,hInstance,0);
HookStates=TRUE;
} else {
UnhookWindowsHookEx(hKeyHook);
HookStates=FALSE;
}
return(0);
}
LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam)
{
static BOOL msflag=FALSE;
if(nCode>=0) {
if(HookStates==TRUE){
if((wParam==0xff)|| //WIN3.X下按鍵值
(wParam==0x5b)||(wParam==0x5c)){//WIN95下按鍵值
if((i==0x15b)||(i==0x15c)){ //按鍵按下處理
msflag=TRUE;
PostMessage(hWndMain,0x7fff,0x1,0x3L);
} else if((i==0xc15b)||(i==0xc15c)){//按鍵抬起處理
msflag=FALSE;
PostMessage(hWndMain,0x7fff,0x2,0x3L);
}
}
}
}
return((int)CallNextHookEx(hKeyHook,nCode,wParam,lParam));
}
該程序的主要功能是監控鍵盤按鍵消息,將兩個特殊按鍵Micro按下和抬起消息轉換成自定義類型的消息,並將自定義消息發送給應用程序主窗口函數。
2、在應用程序主函數中建立窗口後,調用InitHooksDll()函數來初始化動態鏈接庫,並將應用程序主窗口句柄傳遞給鏈接庫,然後調用InstallFilter()函數掛接鍵盤事件監控回調函數。
InitHooksDll(hIMEWnd); //初始化DLL
InstallFilter(TRUE); //安裝鍵盤回調函數
3、在應用程序主窗口函數處理自定義消息時,保存Micro按鍵的狀態,供組合按鍵處理時判斷使用。
switch (iMessage) {
case 0x7fff: //自定義消息類型
if(lParam==0x3L){//設置Micro鍵的狀態
if(wParam==0x1) MicroFlag=TRUE;
else if(wParam==0x2) MicroFlag=FALSE;
}
break;
4、在進行按鍵組合處理時,首先判斷Micro鍵是否按下,然後再進行其它按鍵的判斷處理。
case WM_KEYDOWN: // 按鍵按下處理
if(MicroFlag==TRUE){//Micro鍵按下
if((BYTE)HIBYTE(wParam)==0x5b){
//Micro+"["組合鍵
......//按鍵功能處理
} else if((BYTE)HIBYTE(wParam)==0x5d){
//Micro+"]"組合鍵
......//按鍵功能處理
}
}
break;
5、當應用程序退出時應注意下載鍵盤監控函數,即調用InstallFilter(FALSE)函數一次。
6、利用本文提供的方法設置自己的應用程序功能按鍵,在保證程序功能按鍵不會與其它系統發生沖突的同時,有效地利用了系統中現有資源,而且在實現應用程序功能的同時靈活應用了系統中提供的各種功能調用。