C++/Win32:非前台窗口的键盘输入

The*_*802 1 c++ windows keyboard winapi keyboard-shortcuts

我的目标:

我希望我的应用程序能够响应特定的键盘快捷键/热键,而不管它是否是前台窗口,而不会干扰其他应用程序对这些快捷键的使用。

例如,如果另一个进程有前台窗口并且用户按下该VK_MEDIA_PLAY_PAUSE键,我希望我的应用程序当前前台窗口做出响应。

此功能是用户设置,因此我不担心不受欢迎的行为。

我尝试过但没有奏效的事情:

  1. WM_KEYDOWN 消息仅发送到当前前台窗口,因此我的应用程序不会收到它们。
  2. GetKeyState(),根据MS docs,根据最近检索到的WM_KEY*消息返回密钥的状态,并且因为我的应用程序没有WM_KEYDOWN作为后台窗口接收,所以这个状态不会改变。
  3. GetAsyncKeyState(),根据MS 文档,在以下情况下返回零:

前台线程属于另一个进程,桌面不允许钩子或日志记录。

我不确定这的后半部分是什么意思,但是GetAsyncKeyState()当我的窗口不是前景窗口时返回零。

  1. GetKeyboardState(),如GetKeyState(),仅在检索消息时更改密钥状态。

  2. RegisterHotKey()/ UnregisterHotKey():与其他选项不同,我的应用程序能够响应热键,即使它不是前台窗口;但是,当前的前景窗口不再响应。似乎这些热键仅重定向到我的应用程序,而其他应用程序根本不再看到它们。

我唯一的(凌乱的)想法:

我能想到的唯一方法是那些我相当肯定会让我被 Win32 社区回避的方法。

我可以使用RegisterHotKey()/UnregisterHotKey()来获得热键的通知,然后将按键模拟到当前的前景窗口。看到SendInput()可能会进入循环,我需要先取消注册热键:

case WM_HOTKEY:
     UnregisterHotKey(...);        // unregister the hotkey to avoid looping
     SendInput(...);               // send keyboard input
     RegisterHotKey(...);          // restore it
Run Code Online (Sandbox Code Playgroud)

或直接模拟它SendMessage()

case WM_HOTKEY:
     SendMessage(GetForegroundWindow(), WM_KEYDOWN, ...);
     // send WM_KEYUP ?
Run Code Online (Sandbox Code Playgroud)

这两个似乎都是 Win32 架构并非为之设计的一种黑客行为。

是否有任何(其他)方法可以在不干扰其他应用程序的情况下从非前台窗口读取/接收键盘输入?

Zeu*_*eus 5

您可以使用SetWindowsHookExWH_KEYBOARD_LL参数:

这是一个示例:

#include <windows.h>
#include <iostream>
HHOOK _k_hook;
LRESULT __stdcall k_Callback1(int nCode, WPARAM wParam, LPARAM lParam)
{
    PKBDLLHOOKSTRUCT key = (PKBDLLHOOKSTRUCT)lParam;
    //a key was pressed
    if (wParam == WM_KEYDOWN && nCode == HC_ACTION)
    {
        switch (key->vkCode)
        {
        case VK_ESCAPE:
            puts("ESC pressed");
            break;
        case 'A':
            puts("A pressed");
            break;
        case VK_RETURN:
            puts("RETURN pressed");
            break;
        }
    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);
}


int main()
{
    _k_hook = SetWindowsHookEx(WH_KEYBOARD_LL, k_Callback1, NULL, 0);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) != 0)
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    if (_k_hook)
        UnhookWindowsHookEx(_k_hook);
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理如下:

在此处输入图片说明

  • @TheTrueJard尝试使用`VK_LCONTROL`,`VK_RCONTROL`,`VK_LSHIFT`和`VK_RSHIFT` (2认同)