为什么我的低级窗口钥匙钩停止工作

Why does my low level windows key hook stop working?

本文关键字:钥匙 停止工作 窗口 我的 为什么      更新时间:2023-10-16

这是我最初问题的延续为什么在我的DX11游戏中加载了D3D10SDKLayers.dll?我正在创建一个DX11游戏,并使用低级别的windows键钩来捕捉Alt+Enter,这样我就可以使用自己的方法切换全屏,而不是让windows自动切换,这不可避免地会导致问题。有关此过程的描述和详细信息,请参阅链接的问题。我的问题是,由于某种原因,在第6次Alt+Enter之后,键钩一直停止工作。我自己并没有注销它。

这是关键挂钩代码:


LRESULT _stdcall MyClass::WindowsKeyHook( s32 nCode, WPARAM wParam, LPARAM lParam ) {
    printf("Key hook called, nCode: %d. ", nCode);
    if( nCode < 0 || nCode != HC_ACTION )  { // do not process message 
        return CallNextHookEx( MyClassVar.GetWindowsKeyHook(), nCode, wParam, lParam );
    }
    printf(" Key hook status ok.n");
    BOOL bEatKeystroke = FALSE;
    KBDLLHOOKSTRUCT* p = ( KBDLLHOOKSTRUCT* )lParam;
    switch( wParam ) {
        //NOTE: Alt seems to be a system key when it is PRESSED, but a regular key when it is released...
        case WM_SYSKEYDOWN:
            if(p->vkCode == VK_MENU || p->vkCode == VK_LMENU || p->vkCode == VK_RMENU) {
                MyClassVar.SetAltPressed(TRUE);
            }
            if(MyClassVar.IsAltPressed() && p->vkCode == VK_RETURN) {
                bEatKeystroke = TRUE;
                MyClassVar.SetAltEnterUsed(TRUE);
                printf("Alt+Enter used.n");
            }
            break;
        case WM_SYSKEYUP:
            //NOTE: releasing alt+enter causes a SYSKEYUP message with code 0x13: PAUSE key...
            break;
        case WM_KEYDOWN:
            break;
        case WM_KEYUP: {
            if(p->vkCode == VK_MENU || p->vkCode == VK_LMENU || p->vkCode == VK_RMENU) {
                MyClassVar.SetAltPressed(FALSE);
            }
            bEatKeystroke = ( !MyClassVar.IsShortcutKeysAllowed() &&
                                ( p->vkCode == VK_LWIN || p->vkCode == VK_RWIN ) );
            break;
        }
    }
    if( bEatKeystroke ) {
        return 1;
    }
    else {
        return CallNextHookEx( MyClassVar.GetWindowsKeyHook(), nCode, wParam, lParam );
    }
}

如果你需要更多的信息,告诉我需要什么。我不知道为什么会发生这种情况,所以我不确定我需要提供什么样的信息。据我所知,除了明确注销密钥挂钩之外,摆脱密钥挂钩的唯一方法是Windows将其超时。所有MyClassVar方法都是内联的,以尽可能快,并且Alt+Enter是从一个单独的线程处理的。

据我所知,除了明确注销密钥挂钩之外,摆脱密钥挂钩的唯一方法是Windows将其超时。

这是正确的,您可以通过临时增加LowLevelHooksTimeout注册表项来确认它的超时。

也有可能,但极不可能是另一个键盘挂钩先到达那里,并在你的挂钩被调用之前吞下所有输入(这正是你的挂钩试图对某些组合键进行的操作)。

从你的评论中,当DXGI为你切换到全屏时,听起来你想选择全屏分辨率。

以下是DirectX图形基础结构(DXGI)的摘录:最佳实践。这些信息可能有帮助,也可能没有帮助,我在摘录之前删除的关于WM_SIZE的内容也是如此。

前面解释的方法遵循一条非常特殊的路径。DXGI默认情况下将全屏分辨率设置为桌面分辨率。然而,许多应用程序会切换到首选的全屏分辨率。在这种情况下,DXGI提供IDXGISwapChain::ResizeTarget。这应该在调用SetFullscreenState之前调用。尽管可以按相反的顺序调用这些方法(先调用SetFullscreenState,然后调用ResizeTarget),但这样做会导致向应用程序发送额外的WM_SIZE消息。(这样做也可能导致闪烁,因为DXGI可能会被迫执行两次模式更改。)调用SetFullscreenState后,建议在RefreshRate成员为零的情况下再次调用ResizeTarget。这相当于DXGI中的无操作指令,但它可以避免刷新率问题,下面将讨论这些问题。

DXGI概述有更多关于这件事的信息,ResizeTarget可能没有人们希望的那么有用:

默认情况下,DXGI会选择包含窗口大部分客户端区域的输出。这是DXGI在响应alt-enter进入全屏时唯一可用的选项。

然而,它确实提到,大小是由窗口的客户端区域决定的。也许你想限制客户端区域,而不是安装键盘挂钩?

您是否尝试禁用DXGI的挂钩?

IDXGISwapChain::MakeWindowAssociation( DXGI_MWA_NO_WINDOW_CHANGES )

此外,使用DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH创建交换链将使DXGI找到与您的窗口大小最匹配的显示模式。如果你已经将ResizeTarget()设置为640x480,并且至少有一些640x480模式可用,那么你应该得到它。然后你会得到一个WM_SIZE设置为640x480,这是你调用ResizeBuffers的机会。如果像这样调用ResizeBuffers,在模式切换后,它将启用翻页(新的后缓冲区必须与监视器关联,否则系统无法直接翻页)。如果没有像这样启用翻页,那么所有的Present调用都将调用一个blt操作,该操作将占用您不需要花费的带宽。

请注意,您可能会得到640x480,也可能不会。例如,如果旋转显示器,你可能会得到下一个最大的模式,它将包含640x480,可能是768x1024。如果你想在旋转显示器上工作得很好,你可以注意这一点,并将自己的信箱输入到你想要的4x3纵横比中。

但通常最好的做法是让DXGI选择显示模式,因为桌面模式可能是最好的模式有很多原因。用户可能已经将电脑连接到一个只能执行该模式的投影仪。一些液晶显示器将以原生分辨率显示640x480,即显示器中间有一个小小的邮票。理想情况下,你应该对你的应用程序进行编码,使其基本上在任何分辨率下都能看起来很漂亮,但至少在4x3、3x4、6x9和9x6纵横比下。

快乐编码-Jeff