问题
My goal:
I want my application to be able to respond to specific keyboard shortcuts/hotkeys regardless of whether it is the foreground window without interfering with other applications' use of these shortcuts.
For instance, if another process has the foreground window and the user presses the VK_MEDIA_PLAY_PAUSE
key, I want my application and the current foreground window to respond.
This function is a user setting, so I'm not concerned about undesired behavior.
Things I've tried that haven't worked:
WM_KEYDOWN
messages are only sent to the current foreground window, so my application does not receive them.GetKeyState()
, according to the MS docs, returns the status of the key according to the most recently retrievedWM_KEY*
messages, and because my application doesn't receiveWM_KEYDOWN
as background window, this status doesn't change.GetAsyncKeyState()
, according to the MS docs, returns zero when:
The foreground thread belongs to another process and the desktop does not allow the hook or the journal record.
I'm not sure what the second half of this means, but GetAsyncKeyState()
returns zero when my window is not the foreground window.
GetKeyboardState()
, likeGetKeyState()
, only changes key status as messages are retrieved.RegisterHotKey()
/UnregisterHotKey()
: Unlike the other options, my application is able to respond to hotkeys even when it's not the foreground window; however, the current foreground window no longer responds. It appears that these hotkeys are redirected to only my application, and other applications no longer see them at all.
My only (messy) idea:
The only approaches I can think of are ones that I'm fairly certain would have me shunned by the Win32 community.
I could use RegisterHotKey()
/UnregisterHotKey()
to be notified of the hotkey, and then emulate the keypress to the current foreground window. Seeing as SendInput()
would likely enter a loop, I would need to either unregister the hotkey first:
case WM_HOTKEY:
UnregisterHotKey(...); // unregister the hotkey to avoid looping
SendInput(...); // send keyboard input
RegisterHotKey(...); // restore it
or emulate it directly with SendMessage()
:
case WM_HOTKEY:
SendMessage(GetForegroundWindow(), WM_KEYDOWN, ...);
// send WM_KEYUP ?
Both of these seem like a hack that the Win32 architecture was not designed for.
Is there any (other) way to read/receive keyboard input from a non-foreground window without interfering with other applications?
回答1:
You can use SetWindowsHookEx with WH_KEYBOARD_LL
parameter:
Here is a sample:
#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;
}
It works as follows:
来源:https://stackoverflow.com/questions/65210101/c-win32-keyboard-input-to-a-non-foreground-window