创建隐形和无模式属性表会导致焦点变化?

时间:2015-03-25 22:01:25

标签: winapi mfc

我正在调查与失去焦点和更改窗口激活相关的问题。我发现如果我创建一个不可见的属性表,活动/前景窗口会改变,焦点窗口也会改变。以下是一些示例MFC代码:

   // ignore CAutoDeleter, just a template that calls "delete this " in PostNcDestroy()
   CPropertySheet* pSheet = new CAutoDeleter<CPropertySheet>(_T("Test Sheet"));
   CTestPage* pPage = new CAutoDeleter<CTestPage>();
   pSheet->AddPage(pPage);

   DWORD style = WS_SYSMENU | WS_POPUP | WS_CAPTION | DS_MODALFRAME | DS_CONTEXTHELP;
   // style |= WS_DISABLED; //does nothing to help

   DWORD exStyle = 0;
   //exStyle = WS_EX_NOPARENTNOTIFY|WS_EX_NOACTIVATE; // does nothing to help
   pSheet->Create(AfxGetMainWnd(), style, exStyle); // adding 

调用pSheet-&gt; Create()后,活动/前景/焦点窗口已更改,应用程序窗口位于顶部。如果我使用Spy ++并查看创建的窗口,则会发现属性表是DIALOG窗口类。我假设它有一个不同的WNDPROC,当然。有趣的是,如果我使用创建一个不可见的无模式对话框,它就不会出现问题。如果我创建了不可见的无模式对话框,则活动/前景/焦点窗口保持不变。

我尝试在代码片段中设置各种标记,但是它们没有任何可辨别的效果 - 我仍然没有闪烁和激活。

我可以通过在pSheet->Create()之前和之后设置和清除挂钩(WH_CBT)来获得一些改进 - 然后吃掉激活消息。当我这样做时,我没有可怕的闪烁,我的应用程序窗口没有出现在顶部。但是,焦点(以及具有插入符号的窗口的插入符号)确实远离Create()之前的任何窗口。

有没有人知道在创建隐藏属性表时保持焦点和激活不变的方法? (在某些时候,属性表可能会或可能不会被设置为可见。并且,如果属性表在被销毁时不可见,它也会导致闪烁和激活更改。)

我尝试使用GetUIThreadInfo()中返回的值来尝试在调用Create()之后恢复内容,但它也会导致一些闪烁。

我只想知道如何创建一个不会导致活动,前景和焦点窗口发生变化的不可见属性表。

2 个答案:

答案 0 :(得分:2)

不幸的是,底层API函数PropertySheet在创建后在主属性表对话框中调用SetForegroundWindow。没有简单的方法 - 使用WH_CBT钩子的你的kludge可能是你最好的选择。

修改:正如@stephen在this duplicate question的评论中所建议的那样,您可以使用LockSetForegroundWindow阻止激活/焦点更改。

答案 1 :(得分:2)

我几周来一直在努力解决同样的问题,有一个类似的项目,我的主应用程序启动一个辅助EXE进程,充当音频应用程序中的服务器(这需要一个单独的进程来屏蔽应用程序插件故障,因此它可以是实时音频处理的高优先级。)

我的辅助EXE是一个无模式CPropertySheet,用于状态和诊断显示,但是打算隐藏在后台启动。然而,无论我尝试了哪些变通方法(例如覆盖OnWindowPosChanging),它总是在启动时从主应用程序中窃取激活(

})。

我以为我会发疯,所以我很高兴找到这个问题。 WH_CBT技巧很有用,但我发现虽然它阻止了激活辅助EXE,但它并没有阻止主应用程序的停用。

但后来我在LockSetForegroundWindow API(自Win2K以来可用)中发现了一个出色的解决方案,这是我以前从未听说过的。看起来它正是为了这个目的,禁用前台激活的更改并防止对等进程窃取它。

https://msdn.microsoft.com/en-us/library/windows/desktop/ms633532(v=vs.85).aspx

它可以很好地消除在属性表公共控件深处发生的对SetForegroundWindow的内部调用,并且无论是在CreateProcess之前的主应用程序中还是在辅助EXE中使用,都可以正常工作。我选择后一种情况来包装属性表创建:

LockSetForegroundWindow(LSFW_LOCK);
pSheet->Create(NULL, dwStyle, dwExStyle);
LockSetForegroundWindow(LSFW_UNLOCK);

这最大限度地减少了干预的范围,并将修复本地化为作为问题根源的流程。我希望这可以挽救其他人在这个繁琐的问题上浪费尽可能多的时间。