第一次在这里发帖,我一直在纠结这个问题的正确解决方法。
我有一个自己的UI引擎,它有自己的键盘处理功能,并且正在用它来显示一个游戏覆盖层。游戏叠加本身对键盘和窗口事件都是透明的,为了尽量减少对游戏的干扰,但为了让叠加本身具有交互性,我需要借助键盘和鼠标钩子来阻止一些事件到达游戏。对于鼠标输入来说,这是很琐碎的,而且效果完美。我遇到问题的是低级别的键盘钩子。
在这一点上,我有一些在大多数情况下可以使用的东西。我设法解决了几个涉及死键和不良输入的问题,但从来没有设法创建一个可以主动阻止键盘输入到游戏中的钩子--有些东西总是出错。
例如,当用户试图在覆盖层上的文本框中写一些文本,并且不希望游戏处理相同的按键时,主动阻止键盘输入将是最有用的。
我目前的问题是,如果我在钩子过程中通过返回一个非零值来阻止键盘输入,那么覆盖层的UI引擎就会停止感知 Ctrl 键的状态,导致无法复制粘贴到覆盖层的文本框中。有趣的是,在 Alt-Tab锭,一切都很好,但在这之后, Ctrl 钩子抓取从 VK_CONTROL
到 VK_LCONTROL
. 更有趣的是,无论是 GetKeyState(VK_CONTROL)
也不 GetAsyncKeyState(VK_CONTROL)
也不 GetAsyncKeyState(VK_LCONTROL)
在UI端注册 Ctrl 键为按下。
由于多年的实验和变通,下面的键盘钩子的代码有点乱。我会尽可能地注释它。
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 );
}
UI引擎本身会检查 Ctrl, 换挡 和 祭祀 国家通过 GetKeyState()
因为通过跟踪这些 WM_SYSKEYDOWN
比如说,这些信息会导致 Alt-Tab 具有 祭祀 键卡住了,因为窗口永远不会收到 WM_SYSKEYUP
消息。的状态的函数。Ctrl换挡祭祀 键被调用在几个不同的 WM_...
必要的时候,会有消息。然而,一旦 VK_LCONTROL
信息开始被键盘钩子拦截,而不是被键盘钩子拦截。VK_CONTROL
的,这个功能总是报告所有的键都没有按下。
你可以试试不同的方法。如果你的叠加是这期间的活动窗口,那么你可以不用钩子处理键盘和鼠标事件,如果你想把一个事件转发给游戏,你可以直接为游戏窗口合成事件。