【问题标题】:Windows: How to query state of modifier keys within low level keyboard hook?Windows:如何在低级键盘挂钩中查询修饰键的状态?
【发布时间】:2013-09-25 21:32:39
【问题描述】:

对于 USB 键盘配置工具,我需要拦截所有键盘输入并检测同时按下了哪些修改键和普通键。因此,我使用了一个 Windows 低级挂钩 (WH_KEYBOARD_LL),它可以正常工作,只是我无法确定是否按下了 WIN-Key (VK_LWIN / VK_RWIN)(控制/移位和 alt 正在工作)。

我做了一个小命令行工具来显示问题:

#include <Windows.h>
#include <iostream>

using namespace std;

HHOOK hKeyboardHook;


LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if (nCode < 0 || nCode != HC_ACTION )
        return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);

    KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;

    if(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
    {
        // working
        if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
            cout << "CONTROL" << endl;
        if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
            cout << "SHIFT" << endl;
        if(GetAsyncKeyState(VK_MENU) & 0x8000) // ALT
            cout << "ALT" << endl;

        // VK_xWIN not working at all
        if((GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000))
            cout << "WIN" << endl;

        // working for ALTGR/right-handed ALT
        if((GetAsyncKeyState(VK_LCONTROL) & 0x8000) || (GetAsyncKeyState(VK_RCONTROL) & 0x8000))
            cout << "LRCONTROL" << endl;

        // not working at all
        if((GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000))
            cout << "LRSHIFT" << endl;
        if((GetAsyncKeyState(VK_LMENU) & 0x8000) || (GetAsyncKeyState(VK_RMENU) & 0x8000))
            cout << "LRMENU" << endl;
    }

    //return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);
    return 1;
}



int main(int argc, char* argv[])
{
    hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0 );

    MSG message;
    while (GetMessage(&message,NULL,0,0)) {
        TranslateMessage( &message );
        DispatchMessage( &message );
    }

    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

如果我从 LowLevelKeyboardProc 返回“1”,则每次按键都会被“吞下”(CTRL+ALT+DEL 和 WIN+L 除外)。如果我在回调函数的末尾调用下一个钩子,则行为会发生变化(并且键显然不再被吞下)。然后,如果 WIN 键与另一个键一起按下,我会得到按下 WIN 键的信息。

如何拦截所有键盘输入并检测 WIN 键按下(使用 GetAsyncKeyState)?还是有其他方法可以让所有(inkl.WIN)按下修饰键?

【问题讨论】:

    标签: c++ windows keyboard hook


    【解决方案1】:

    LowLevelKeyboardProc 的文档对返回值进行了如下说明:

    如果钩子程序处理了消息,它可能会返回一个非零值到 防止系统将消息传递给钩子的其余部分 链或目标窗口过程。

    所以,return 1 说“我已经处理了这些信息,并且没有其他任何事情应该用这些键做”。

    我不确定您如何解决自己检测 WIN 键的问题 - 我怀疑系统只是在按下第二个键之前不会转发具有键盘事件的问题。

    【讨论】:

    • “所以,return 1 表示“我已经处理了这些信息,没有其他任何事情应该用这些键做”。这就是我想要的。我只是想表明处理下一个钩子以某种方式“修复”了该功能。也许其他人可以使用这些信息为我找到解决方案;-)
    • 这不是“修复它”,调用 next 钩子将返回该钩子的值 - 大概是 0,这意味着系统一旦到达那么远就会处理密钥。如果返回 1,则不会调用“下一个”进程,并且系统会丢弃该事件,因为它已被处理。换句话说,您所看到的正是您应该期望看到的。或者换一种说法,“它不是调用改变行为的下一个钩子,而是返回 1”。如果你这样做dummy = CallNextHookEx(...); return 1;,你会看到同样的事情。
    • 好的,我明白了。但这并不能解决问题。来自CallNextHookEx 的文档:“除非您绝对需要防止通知被其他应用程序看到,否则您应该调用 CallNextHookEx。”这正是我需要的。作为副作用,它会阻止在后续挂钩中处理 WIN 键(只是一个疯狂的猜测)。
    • 那么,大概您只想在某些情况下这样做?所以你不应该无条件地return 1,而是有类似“if (some_condition) return 1; else return CallNextHookEx(...);`
    • 是的,但我用另一种方式做:当某个编辑字段获得焦点时,我的钩子被安装(当它失去焦点时被卸载)。所以我不必在钩子内决定,钩子链会变短一点。 (编辑字段用于显示按下的键/快捷键)。非常感谢您的帮助,Mats。
    【解决方案2】:

    我不知道为什么我无法从钩子中获取 windows 键的状态,但我实现了一个简单的解决方法,我不想对你隐瞒。

    如果我按下/释放 windows 键,回调函数就会被调用。所以我只是自己保存键的状态,并在接下来的按键操作中使用这些信息。

    所以修改后的回调函数如下:

    bool leftWinKeyPressed = false;
    bool rightWinKeyPressed = false;
    
    LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
    {
        if (nCode < 0 || nCode != HC_ACTION )
            return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);
    
        KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;
    
        // save state of win keys manually... (needs to be tested some more)
        if(p->vkCode == VK_LWIN)
            leftWinKeyPressed = (wParam == WM_KEYDOWN)?true:false;
        else if(p->vkCode == VK_RWIN)
            rightWinKeyPressed = (wParam == WM_KEYDOWN)?true:false;
    
        if(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
        {
            // not beautifull but working...
            if(leftWinKeyPressed || rightWinKeyPressed)
                cout << "WIN" << endl;
    
            // for example
            if(leftWinKeyPressed && p->vkCode == 68 )
                cout << "LEFT WINDOWS + D";
        }
    
        return 1;
    }
    

    为了清理它,我可能会对所有修饰键执行此操作,并使用位字段来存储修饰键的状态。所以我不依赖GetAsyncKeyState,这是一种奇怪的行为。但如果有人发现它为什么会这样,请告诉我。

    【讨论】:

    • 如何防止以下VK_LWIN WM_KEYUP 打开Windows 8 Metro 菜单?如果我检测到VK_LWINWM_KEYDOWN,那么快捷键,我有两个选择:为VK_LWINWM_KEYUP返回1,这将导致每个键触发WIN快捷键,或者返回CallNextHookEx这将打开 Windows 8 Metro 菜单。有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 2011-08-08
    • 1970-01-01
    相关资源
    最近更新 更多