Winapi 图尼科德不';不能返回正确的字符

Winapi 图尼科德不';不能返回正确的字符,winapi,keyboard,hook,keyboard-hook,Winapi,Keyboard,Hook,Keyboard Hook,我试图在低级键盘挂钩中调用ToUnicode,并打印它返回的字符。但是,该函数似乎没有考虑是否按下了shift或caps lock等特殊键,因此输出与MapVirtualKey函数相同,当前键的虚拟代码作为参数传递 例如(pressed keys=>ToUnicode返回的字符): 如何调用函数(在钩子过程中): 它还正确返回使用AltGr按下的键盘区域设置相关键,例如[AltGr]a=>ą 上述示例并不完全正确,因为出现了另一个问题-如果按下caps lock,下一个字符仍取决于其先前的状态,

我试图在低级键盘挂钩中调用
ToUnicode
,并打印它返回的字符。但是,该函数似乎没有考虑是否按下了shift或caps lock等特殊键,因此输出与
MapVirtualKey
函数相同,当前键的虚拟代码作为参数传递

例如(
pressed keys=>ToUnicode返回的字符
):

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

它还正确返回使用AltGr按下的键盘区域设置相关键,例如
[AltGr]a=>ą

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

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()
修复了其中一个问题,以及后一个大写锁(和其他特殊钥匙)问题的原因吗?

部分答案:

Windows文档建议
GetKeyboardState
GetKeyState
为相应的键返回类似的结果,当在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);
}
HHOOK-hook;
LRESULT回调钩子_过程(int代码、WPARAM WPARAM、LPARAM LPARAM)
{
如果(代码==HC\U动作)
{
if(wparam==WM_KEYDOWN)
{
KBDLLHOOKSTRUCT*kb=(KBDLLHOOKSTRUCT*)LPRAM;
字节状态[256]={0};
wchar_t str[10]={0};
GetKeyState(VK_移位);
GetKeyState(VK_菜单);
GetKeyboardState(状态);
如果(字节->vkCode,字节->扫描码,
状态,str,sizeof(str)/sizeof(*str)-1,0)>0)
{

如果(kb->vkCode==VK_RETURN)std::wcout同时调用
GetKeyState(VK_SHIFT)
GetKeyState(VK_大写)
在调用
GetKeyboardState
之前。您的密码不清楚。您是否正在响应
WM\u KEYDOWN
?@Barmak Shemirani是的,我正在响应
WM\u KEYDOWN
。我会在回家后更改密码,所以在几个小时内。@BarmakShemirani它现在工作得很好,谢谢。但是这些调用实际上有什么作用呢?我相信de>GetKeyState(键)
在内部更新
key
的状态,对吗?我找不到任何关于msdn的线索。您应该将您的评论作为答案发布。谢谢!显示
ToUnicode
返回的字符的一个注释-根据msdn:
但是,缓冲区可能包含的字符数超过返回值指定的字符数。发生这种情况时,任何xtra字符无效,应该忽略。
,因此如果只显示
N
字符就好了,其中
N
ToUnicode
@Jason返回的值,是的,这很有意义。这也适用于
AltGr
吗?如果您的意思是死键是否生成单个字符,答案是肯定的,但是仅当死键可以与另一个按下的键组合时-根据msdn:
最常见的原因[即ToUnicode缓冲区包含>=2个字符]是死键字符(重音或变音符号)存储在键盘布局中的虚拟键不能与指定的虚拟键组合成单个字符。
顺便说一句,如果我们必须遵守规则,在截取任何键之前,您应该
返回CallNextHookEx
if
code<0
,如hook proc文档中所述。此外,如果复制了8个字符s到str缓冲区,则
str[8]=0
将导致未定义的行为。但是:
此缓冲区可能返回而不以null结尾,即使变量名表明它以null结尾。
。好的,我为缓冲区添加了一个额外的索引。
abcd => abcd (correct)
[caps lock]abcd => ABCD (correct)
ab[holding shift]cd => abCD (correct)
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)
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);
}