0

这是我最初的问题的延续, 为什么在我的 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 是从单独的线程处理的。

4

2 回答 2

2

据我所知,除了显式取消注册之外,摆脱密钥挂钩的唯一方法是如果 Windows 超时。

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

也有可能,但极不可能有另一个键盘钩子首先到达那里并在调用钩子之前吞下所有输入(这正是你的钩子试图用某些组合键做的事情)。

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

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

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

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

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

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

于 2012-01-26T22:10:38.707 回答
1

你试过禁用DXGI的钩子吗?

IDXGISwapChain::MakeWindowAssociation( DXGI_MWA_NO_WINDOW_CHANGES )

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

请注意,您可能会或可能不会获得 640x480。例如,如果显示器旋转,您可能会得到下一个最大的模式,其中包含 640x480,可能是 768x1024。如果您想在旋转的显示器上正常工作,您可以注意这一点,并将自己的信箱设置为您想要的 4x3 纵横比。

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

快乐编码 -Jeff

于 2012-06-11T20:43:39.407 回答