首先讓我們看看HOOK函數是怎麽安裝、調用和刪除的。應用程序通常是調用SetWindowsHookEx()函數來進行安裝的,其函數的原型如下:
SetWindowsHookEx(
Int idHook;
HOOKPROC lpfn;
HINSTANCE hMod;
DWORD dwThreadId;
);
參數說明:
idHook 是”鉤子”的類型,”鉤子”的類型壹***有13種,具體如下表:
“鉤子”類型
解釋
WH_CALLWNDPROC
系統將消息發送到指定窗口之前的“鉤子”
WH_CALLWNDPROCRET
消息已經在窗口中處理的“鉤子”
WH_CBT
基於計算機培訓的“鉤子”
WH_DEBUG
差錯“鉤子”
WH_FOREGROUNDIDLE
前臺空閑窗口“鉤子”
WH_GETMESSAGE
接收消息投遞的“鉤子”
WH_JOURNALPLAYBACK
回放以前通過WH_JOURNALRECORD“鉤子”記錄的輸入消息
WH_JOURNALRECORD
輸入消息記錄“鉤子”
WH_KEYBOARD
鍵盤消息“鉤子”
WH_MOUSE
鼠標消息“鉤子”
WH_MSGFILTER
對話框、消息框、菜單或滾動條輸入消息“鉤子”
WH_SHELL
外殼“鉤子”
WH_SYSMSGFILTER
系統消息“鉤子”
lpfn 指向“鉤子”過程的指針。
hMod “鉤子”過程所在模塊的句柄。
dwThreadId “鉤子”相關線程的標識。
通常我們都是把”鉤子”做成動態鏈接庫,這樣的好處是可以是系統內的每個進程訪問。但是也可以在系統中直接調用,我的建議還是用動態庫。如果用動態庫的話,那麽SetWindowsHookEx()中的第三個參數就是該動態鏈接庫模塊的句柄;對於壹個只供單個進程訪問的”鉤子”,可以將其”鉤子”過程放在安裝”鉤子”的同壹個線程內,此時SetWindowsHookEx()中的第三個參數為該線程的hInstance。安裝”鉤子”有兩種方法:1.妳可以把他做成動態連接庫文件,和程序壹起編譯。2.妳可以在程序的任何地方直接調用。第2種的方法太麻煩,我不建議用,在這裏我就不詳細介紹啦。相比之下第1種比較簡單。其”鉤子”的過程都在動態鏈接庫內完成。SetWindowsHookEx()函數是壹個安裝函數,如故壹個由某種類型的”鉤子”監視的事件發生,系統就會調用相應類型的”鉤子”鏈開始處的”鉤子”過程,”鉤子”鏈的每個”鉤子”過程都要考慮是否把事件傳遞給下壹個”鉤子”過程。如果要傳遞的話,就要調用CallNestHookEx()函數。這個函數成功時返回”鉤子”鏈中下壹個”鉤子”過程的返回值,返回值的類型依賴於”鉤子”的類型。這個函數的原型如下:
LRESULT CallNextHookEx(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
);
其中hhk為當前”鉤子”的句柄,由SetWindowsHookEx()函數返回。NCode為傳給”鉤子”過程的事件代碼。wParam和lParam 分別是傳給”鉤子”過程的wParam值,其具體含義與”鉤子”類型有關。
釋放”鉤子”
釋放”鉤子”比較簡單,他只有壹個參數。當不在需要”鉤子”時,應及時將其釋放。他是調用UnhookWindowsHookEx()函數來實現的,函數原型如下:
UnhookWindowsHookEx(
HHOOK hhk;
);
函數成功返回TRUE,否則返回FALSE。
如果我這樣講您還是不明白的話,請看下面給出的壹些典型“鉤子”代碼和說明。
LRESULT WINAPI CallWndProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if(nCode<0)
return CallNextHookEx(NULL,nCode,wParam,lParam);
switch(nCode)
{
case HC_ACTION:
//”鉤子”程序要處理什麽的代碼
break;
default:
break;
}
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
這是WH_CALLWNDPROC”鉤子”的代碼,此”鉤子”允許程序監視由函數SendMessage發送給窗口過程的消息。系統將消息發送到目的窗口之前調用WH_CALLWNDPROC “鉤子”過程。
LRESULT WINAPI CallwndProc(int nCode,WPARAM,wParam,LPARAM lParam)
{
if(nCode<0) return callNextHookEx(NULL,nCode,wParam,lParam);
switch(nCode)
{
case HC_ACTION:
switch(wParam)
{
Case PM_REMOVE:
//某個應用程序調用了GetMessage函數或者是帶PM_REMOVE參數的//PeekMessage函數,從消息隊列中移去壹個消息。
Break;
Case PM_NOREMOVE:
//某個應用程序以PM_NOREMOVE為參數調用PeekMessage函數
break;
default:
break;
}
break;
default:
break;
}
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
這是調用WH_GETMESSAGE的函數,此函數允許應用程序監視函數GetMessage和 PeekMessage返回的消息。應用程序可以用鉤子WH_GETMESSAGE來監視鼠標和鍵盤的輸入以及其他系統發送到消息隊列中的消息。
LRESULT CALLBACK CBTProc(int nCode,WPARAM wParam,LPARAM lParam)
{
If(nCode<0) Return callNextHookEx(NULL,nCode,wParam,lParam);
Switch(nCode)
{
case HCBT_ACTIVATE:
//系統將激活壹個窗口
break;
case HCBT_CLICKSKIPPED:
//系統從系統消息隊列中移去壹個鼠標消息
break;
case HCBT_CREATEWND:
//系統將創建壹個窗口
break;
case HCBT_DESTROYWND:
//系統將關閉壹個窗口
break;
case HCBT_KEYSKIPPED:
//系統從系統消息隊列中移去壹個鍵盤消息
break;
case HCBT_MINMAX:
//系統將最大化或最小化壹個窗口
break;
case HCBT_MOVESIZE:
//系統將移動壹個窗口或改變壹個窗口的大小
break;
case HCBT_QS:
//系統在系統消息隊列中檢索到WM_QUEUESYNC消息
break;
case HCBT_SETFOCUS:
//系統設置鍵盤輸入窗口
break;
case HCBT_SYSCOMMAND:
//將要執行壹個系統命令
break;
default:
//可以添加其他代碼
break;
}
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
每種”鉤子”類型都有其對應的函數,這些函數的參數都是壹樣的,有興趣的朋友可以在MSDN中找的他們的詳細說明。
下面我給出壹個完整的”鉤子”安裝和刪除的過程的代碼。
#include "stdafx.h"
#include "hook.h"
HINSTANCE hInstance;
HHOOK hhkKeyboard;
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
hInstance=(HINSTANCE)hModule;
return TRUE;
}
LRESULT KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
MessageBeep(-1);
return CallNextHookEx(hhkKeyboard,nCode,wParam,lParam);
}
HOOK_API BOOL EnableKeyboardCapture()
{
if(!(hhkKeyboard=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hInstance,0)))
return FALSE;
return TRUE;
}
HOOK_API BOOL DisableKeyboardCapture()
{
return UnhookWindowsHookEx(hhkKeyboard);
}
註意:這是壹個動態鏈接庫文件。
在程序中要想調用“鉤子”的時候,有EnableKeyboardCapture()函數就可以啦,但妳按鍵的時候就回發出聲音。