WIN API键盘钩子按键记录器:GetMessage阻塞与定时停止问题
问题原因分析与解决方案
核心原因
WH_KEYBOARD_LL是Windows的低级别键盘钩子,它的工作逻辑和普通钩子有本质区别:
- 系统不会直接触发你的钩子回调
kbHook,而是把键盘事件封装成消息,发送到安装钩子的线程的消息队列里。 - 只有当该线程调用
GetMessage、PeekMessage这类消息处理函数时,系统才会在处理消息的间隙触发钩子回调,让你处理按键事件。
你之前的操作完全违背了这个逻辑:
- 设置钩子后直接调用
Sleep(10000),线程休眠期间根本不会处理消息队列,所以钩子回调一次都不会执行,自然生成不了日志。 - 休眠结束后卸载钩子,再进入
GetMessage循环——这时候钩子已经不存在,消息队列里也没有键盘事件消息,GetMessage会一直阻塞等待新消息,程序就僵住了。
正确实现方式
要让程序在指定时间后停止,必须在保持消息循环运行的同时检查超时条件,下面给出两种可行方案:
方案1:在消息循环中检查运行时长
int keylogger_main() { char path[] = "D:\\winApiFiles\\key_logs.txt"; DeleteFileA(path); MSG msg; HHOOK hHook = NULL; time_t start_time = time(NULL); const int RUN_SECONDS = 10; // 设定运行10秒 hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0); if (hHook == NULL) { printf("HOOK FAILED"); return 1; } // 消息循环中同时监控运行时间 while (GetMessage(&msg, NULL, 0, 0)) { // 计算已运行时间,达到设定值就退出循环 if (difftime(time(NULL), start_time) >= RUN_SECONDS) { break; } TranslateMessage(&msg); DispatchMessage(&msg); } UnhookWindowsHookEx(hHook); printf("Key logs saved to: %s", path); return 0; }
方案2:用Windows定时器触发停止
通过SetTimer创建定时器,指定时间后系统会发送WM_TIMER消息,收到消息就退出循环:
// 用全局/静态变量保存钩子句柄,方便后续卸载 static HHOOK g_hHook; int keylogger_main() { char path[] = "D:\\winApiFiles\\key_logs.txt"; DeleteFileA(path); MSG msg; g_hHook = NULL; const int RUN_MS = 10000; // 设定运行10000毫秒(10秒) g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbHook, NULL, 0); if (g_hHook == NULL) { printf("HOOK FAILED"); return 1; } // 设置定时器,RUN_MS毫秒后发送WM_TIMER消息 SetTimer(NULL, 1, RUN_MS, NULL); while (GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_TIMER) { // 收到定时器消息,先销毁定时器再退出循环 KillTimer(NULL, 1); break; } TranslateMessage(&msg); DispatchMessage(&msg); } UnhookWindowsHookEx(g_hHook); printf("Key logs saved to: %s", path); return 0; }
内容的提问来源于stack exchange,提问作者nat34
相关产品推荐
相关产品推荐

