为啥单击子窗口并不总是将应用程序带到前台?

Posted

技术标签:

【中文标题】为啥单击子窗口并不总是将应用程序带到前台?【英文标题】:Why does clicking a child window not always bring the application to the foreground?为什么单击子窗口并不总是将应用程序带到前台? 【发布时间】:2008-09-09 02:58:15 【问题描述】:

当一个应用程序落后于另一个应用程序并且 我点击我的应用程序的任务栏图标,我希望整个应用程序 来到 z-order 的顶部,即使是一个 app-modal,WS_POPUP 对话框 打开。

但是,有些时候,对于我(和其他人)的一些对话框,只有对话框出现在前面;应用程序的其余部分留在后面。

我查看了 Spy++,对于那些正常工作的,我可以看到 WM_WINDOWPOSCHANGING 被发送到对话框的父级。对于那些 留下应用程序的其余部分,WM_WINDOWPOSCHANGING 不是 发送到对话框的父级。

我有一个示例,其中一个对话框通常会带来整个应用程序,而另一个不会。工作对话框和非工作对话框都具有相同的窗口样式、子样式、父窗口、所有者、个体。

简而言之,两者都是使用 DialogBoxParam() 创建的 WS_POPUPWINDOW 窗口, 已将相同的 HWND 作为第三个参数传入。

有没有其他人注意到 Windows 程序中的这种行为异常?当我单击它的按钮时,任务栏会向应用程序发送什么消息?谁负责确保所有应用程序的窗口都出现在前台?

在我的情况下,基本父系是一个 MDI 框架......这是否以某种方式影响?

【问题讨论】:

【参考方案1】:

我知道这已经很老了,但我只是偶然发现它,我知道答案。

在您看到(和编写)的应用程序中,将对话框置于前台并没有将主窗口随之打开,开发人员只是忽略了指定对话框。

这适用于模态窗口,如对话框和消息框,以及非模态窗口。设置无模式弹出窗口的所有者也会使弹出窗口始终高于其所有者。

在Win32 API中,弹出对话框或消息框的函数以所有者窗口为参数:

INT_PTR DialogBox(
    HINSTANCE hInstance,
    LPCTSTR lpTemplate,
    HWND hWndParent,      /* this is the owner */
    DLGPROC lpDialogFunc
);

int MessageBox(
    HWND hWnd,            /* this is the owner */
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType
);

类似,在.NET WinForms中,可以指定所有者:

public DialogResult ShowDialog(
    IWin32Window owner
)

public static DialogResult Show(
    IWin32Window owner,
    string text
) /* ...and other overloads that include this first parameter */

此外,在 WinForms 中,设置无模式窗口的所有者很容易:

public void Show(
    IWin32Window owner,
)

或者,等效地:

form.Owner = this;
form.Show();

在直接的 WinAPI 代码中,可以在创建窗口时设置无模式窗口的所有者:

HWND CreateWindow(
    LPCTSTR lpClassName,
    LPCTSTR lpWindowName,
    DWORD dwStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent, /* this is the owner if dwStyle does not contain WS_CHILD */
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam
);

或之后:

SetWindowLong(hWndPopup, GWL_HWNDPARENT, (LONG)hWndOwner);

或(64 位兼容)

SetWindowLongPtr(hWndPopup, GWLP_HWNDPARENT, (LONG_PTR)hWndOwner);

请注意,MSDN 对SetWindowLong[Ptr] 有以下说法:

不要使用 GWLP_HWNDPARENT 索引调用 SetWindowLongPtr 来更改子窗口的父级。相反,请使用 SetParent 函数。

这有点误导,因为它似乎暗示上面的最后两个 sn-ps 是错误的。这不是这样的。调用SetParent 会将预期的弹出窗口变成父窗口的(设置其WS_CHILD 位),而不是使其成为拥有的窗口。上面的代码是使现有弹出窗口成为拥有窗口的正确方法。

【讨论】:

感谢您的回答!但是,这不是我的应用程序出现问题的原因。正如我在问题中所说,我在所有情况下都通过了所有者。还有其他想法吗? 您是否有一个可重复的样本,偶然?【参考方案2】:

当您单击任务栏图标时,Windows 会向您的应用程序发送 WM_ACTIVATE 消息。

您确定您的代码正在将 WM_ACTIVATE 消息传递给 DefWindowProc 窗口过程进行处理吗?

【讨论】:

【参考方案3】:

对话框的父窗口设置是否正确?

发布此内容后,我启动了自己的 Windows 窗体应用程序并重现了您描述的问题。我有两个对话框,一个可以正常工作,另一个不能正常工作,我看不出任何直接原因是为什么它们的行为不同。如果我知道了,我会更新这篇文章。

Raymond Chen 你在哪里!

【讨论】:

以上是关于为啥单击子窗口并不总是将应用程序带到前台?的主要内容,如果未能解决你的问题,请参考以下文章

一旦按下相关按钮,JQuery 移动弹出窗口并不总是出现

为啥单击属性时未显示文档,但显示在窗口中

更新通过通知点击带到前台的当前活动的正确方法是啥?

MFC:将焦点设置到子窗口时如何防止应用程序成为前台窗口

iOS 私有 API 调用将应用程序带到前台

如何在 OS X 上将进程窗口置于前台?