First post here, I've been stewing over the correct solution to this issue for years.
I have my own UI engine with its own keyboard handling, and am using it to display a game overlay. The game overlay itself is transparent to both keyboard and window events, in order to be minimally obtrusive towards the game, but in order for the overlay itself to be interactive I needed to resort to keyboard and mouse hooks to block some of the events from reaching the game. For mouse input, this is pretty trivial and works perfectly. It's the low level keyboard hook where I ran into issues.
At this point, I have something that's usable in most cases. I managed to work around several issues involving dead keys and bad input, but never managed to create a hook that could actively block keyboard input to the game - something always goes wrong.
Active blocking of keyboard input would, for example, be most useful when the user is trying to write some text in a textbox on the overlay and doesn't want the game processing the same keystrokes.
My current issue is that, if I block the keyboard inputs by returning a nonzero value in the hook process, the UI engine of the overlay stops perceiving the Ctrl key's state, which results in not being able to copy/paste into the overlay's textboxes. Interestingly, before Alt-Tab'ing, everything works fine, but after that, the Ctrl keypress the hook grabs turns from VK_CONTROL
to VK_LCONTROL
. And even more interestingly, neither GetKeyState(VK_CONTROL)
nor GetAsyncKeyState(VK_CONTROL)
nor GetAsyncKeyState(VK_LCONTROL)
on the UI side register the Ctrl key as pressed.
The code for the keyboard hook below is a bit of a mess, due to years of experimentation and workarounds. I'll comment it as best as I can.
LRESULT __stdcall KeyboardHook( int code, WPARAM wParam, LPARAM lParam )
{
// this is an early exit if the game tells me that it actively has focus
if ( disableHooks || mumbleLink.textBoxHasFocus )
return CallNextHookEx( 0, code, wParam, lParam );
// the following two early exits are remnants from earlier experimentation
if ( code < 0 )
return CallNextHookEx( 0, code, wParam, lParam );
if ( wParam != WM_KEYDOWN && wParam != WM_KEYUP && wParam != WM_CHAR && wParam != WM_DEADCHAR && wParam != WM_UNICHAR )
return CallNextHookEx( 0, code, wParam, lParam );
// this checks if either the game or the overlay are in focus and otherwise ignores keyboard input
auto wnd = GetForegroundWindow();
if ( code != HC_ACTION || !lParam || ( wnd != gw2Window && App && wnd != (HWND)App->GetHandle() ) )
return CallNextHookEx( 0, code, wParam, lParam );
// this ignores the overlay itself if it's in focus for some odd reason
if ( App && wnd == (HWND)App->GetHandle() )
return CallNextHookEx( 0, code, wParam, lParam );
KBDLLHOOKSTRUCT *kbdat = (KBDLLHOOKSTRUCT*)lParam;
UINT mapped = MapVirtualKey( kbdat->vkCode, MAPVK_VK_TO_CHAR );
// this bool tests if the overlay has a textbox in focus and the keyboard input should be blocked from propagating further
bool inFocus = App->GetFocusItem() && App->GetFocusItem()->InstanceOf( "textbox" );
// forcefully inject a WM_CHAR message to the overlay's UI engine - never figured out how to trigger a message that would be translated into a WM_CHAR properly
if ( !( mapped & ( 1 << 31 ) ) && !inFocus && wParam == WM_KEYDOWN )
App->InjectMessage( WM_CHAR, mapped, 0 );
if ( inFocus )
{
PostMessage( (HWND)App->GetHandle(), wParam, kbdat->vkCode, 1 | ( kbdat->scanCode << 16 ) + ( kbdat->flags << 24 ) );
/////////////////////////////////////////////////
return 1; // this is where the key input should be blocked, but it causes the mentioned issues with the ctrl key (and probably others too)
/////////////////////////////////////////////////
}
return CallNextHookEx( 0, code, wParam, lParam );
}
The UI engine itself checks the Ctrl, Shift and Alt states through GetKeyState()
because tracking these through WM_SYSKEYDOWN
messages would, for example, result in an Alt-Tab having the Alt key stuck since the window would never receive the WM_SYSKEYUP
message. The function that checks the state of the Ctrl/Shift/Alt keys is called on several different WM_...
messages when necessary. However, as soon as the VK_LCONTROL
messages start being intercepted by the keyboard hook instead of the VK_CONTROL
ones, this function always reports all keys as being unpressed.