这是我原来问题的延续 Why is D3D10SDKLayers.dll loaded during my DX11 game?我正在创建一个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是从一个单独的线程处理的。
答案 0 :(得分:2)
除了明确取消注册之外,摆脱键挂钩的唯一方法是,如果Windows将其用完,据我所知。
这是正确的,你可以暂时确认它暂时超时{。}}。
这也是可能的,但是不太可能另一个键盘钩子首先到达那里并且在你的钩子被调用之前吞下所有输入(这正是你的钩子试图用某些键组合做的事情)。
从您的评论中,您可能会想要在DXGI为您切换到全屏时选择全屏分辨率。
以下是by increasing the LowLevelHooksTimeout
registry key的摘录。这些信息可能会有所帮助,也可能没有帮助,我在此摘录之前已经省略了WM_SIZE
的内容。
前面解释的方法遵循一条非常特殊的路径。 DXGI默认将全屏分辨率设置为桌面分辨率。然而,许多应用程序切换到首选的全屏分辨率。在这种情况下,DXGI提供
IDXGISwapChain::ResizeTarget
。应在调用SetFullscreenState
之前调用此方法。虽然可以按相反的顺序调用这些方法(首先是SetFullscreenState
,然后是ResizeTarget
),但这样做会导致将额外的WM_SIZE
消息发送到应用程序。 (这样做也会导致闪烁,因为可能会强制DXGI执行两次模式更改。)在调用SetFullscreenState
后,建议再次调用ResizeTarget
并将RefreshRate
成员清零。这相当于DXGI中的无操作指令,但它可以避免刷新率的问题,这将在下面讨论。
DirectX Graphics Infrastructure (DXGI): Best Practices有更多关于此事的信息,ResizeTarget
可能没有人们希望的那样有用:
默认情况下,DXGI选择包含窗口大部分客户区的输出。这是DXGI在响应alt-enter时全屏显示的唯一选项。
但是,确实提到大小由窗口的客户区决定。也许你想要DXGI Overview而不是安装键盘钩?
答案 1 :(得分:1)
您是否尝试过禁用DXGI的钩子?
IDXGISwapChain::MakeWindowAssociation( DXGI_MWA_NO_WINDOW_CHANGES )
此外,使用DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
创建交换链将导致DXGI找到与您的窗口大小最匹配的显示模式。如果你已经将ResizeTarget()编辑为640x480,并且至少有一些640x480模式可用,你应该得到它。然后,您将获得一个WM_SIZE到640x480,这是您调用ResizeBuffers的机会。如果像这样调用ResizeBuffers,则在模式切换之后,它将启用页面翻转(新的后台缓冲区必须与监视器关联,否则系统不能直接翻页)。如果没有像这样启用页面翻转,那么所有当前调用都将调用blt操作,这将吸收你不需要花费的带宽。
请注意,您可能会或可能不会获得640x480。例如,如果旋转显示器,您可能会获得包含640x480的下一个最大模式,该模式可能是768x1024。如果你想在旋转的显示器上工作得很好,你可以自己注意这个和信箱你想要的4x3宽高比。
但让DXGI选择显示模式通常是最佳做法,因为桌面模式可能是最佳选择的原因有很多。用户可能已将电脑连接到只能执行该模式的投影仪。一些液晶显示器将以原始分辨率呈现640x480,即显示器中间的青少年邮票。理想情况下,您应该对应用进行编码,以便能够在任何分辨率下看起来非常漂亮,但至少在4x3,3x4,6x9和9x6宽高比上。
快乐的编码 -Jeff