ToUnicode无法返回正确的字符

时间:2018-12-09 22:33:33

标签: winapi keyboard hook keyboard-hook

我试图在低级键盘挂钩中调用ToUnicode并打印返回的字符。但是,该函数似乎没有考虑是否按下了特殊键(例如shift键或大写锁定),因此输出与MapVirtualKey函数相同,并且当前键的虚拟代码作为参数传递。 / p>

例如(pressed keys => characters returned by ToUnicode):

abcd => abcd (correct)
[caps lock]abcd => abcd (wrong: should be ABCD)
ab[holding shift]cd => abcd (wrong: should be abCD)

我如何调用函数(在挂钩过程中):

    KBDLLHOOKSTRUCT* pressedKeyInformation = (KBDLLHOOKSTRUCT*)lParam;

    BYTE keysStates[256]; // 256 bo tyle virtualnych klawiszy wpisze GetKeyboardState

    if(!GetKeyboardState(keysStates))
        //error
    else
    {
        WCHAR charactersPressed[8] = {};

        int charactersCopiedAmount = ToUnicode(pressedKeyInformation->vkCode, pressedKeyInformation->scanCode, keysStates, charactersPressed, 8, 0);

        //std::wcout << ...
    }

后来我注意到,在GetKeyState之前使用任何作为参数传递的虚拟键码(例如VK_RETURNVK_SHIFT)调用ToUnicode会导致它返回正确的字符,例如:

abcd => abcd (correct)
[caps lock]abcd => ABCD (correct)
ab[holding shift]cd => abCD (correct)

它还可以正确返回与键盘区域设置相关的键,然后再按AltGr,例如[AltGr]a => ą

上面的示例并不完全正确,因为出现了另一个问题-例如按下了大写锁定,下一个字符仍然取决于其前一个状态,只有后一个字符会受到影响,例如:

abcd => abcd (correct)
(caps lock is off)[caps lock]abcd => aBCD (wrong: should be ABCD)
(caps lock is off)ab[caps lock]cd => abcD (wrong: should be abCD)

您是否知道GetKeyState(<whatever>)为什么解决了其中一个问题?后一个大写锁定(和其他特殊键)问题的原因是什么?

1 个答案:

答案 0 :(得分:0)

部分答案:

Windows文档建议GetKeyboardStateGetKeyState对于对应的键返回相似的结果,当在Windows消息循环中正确使用键盘消息的Windows消息循环中使用这些功能时,情况就是这样。

但是,在这种情况下,我们具有钩子功能,GetKeyboardState无法正确填充键盘。首先调用GetKeyState,将更改键盘状态,随后对GetKeyboardState的调用将按预期进行。我不知道为什么!

其他奇数,GetKeyState返回SHORT的值,而GetKeyboardState填充BYTE的数组。但这没有什么区别,因为我们只对高位和低位感兴趣。

HHOOK hook;
LRESULT CALLBACK hook_procedure(int code, WPARAM wparam, LPARAM lparam)
{
    if(code == HC_ACTION)
    {
        if(wparam == WM_KEYDOWN)
        {
            KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT*)lparam;
            BYTE state[256] = { 0 };
            wchar_t str[10] = { 0 };
            GetKeyState(VK_SHIFT);
            GetKeyState(VK_MENU);
            GetKeyboardState(state);
            if (ToUnicode(kb->vkCode, kb->scanCode, 
                state, str, sizeof(str)/sizeof(*str) - 1, 0) > 0)
            {
                if(kb->vkCode == VK_RETURN) std::wcout << "\r\n";
                else std::wcout << str;
            }
        }
    }
    return CallNextHookEx(hook, code, wparam, lparam);
}