关于winapi:为什么单击子窗口并不总是将应用程序置于前台?

关于winapi:为什么单击子窗口并不总是将应用程序置于前台?

Why does clicking a child window not always bring the application to the foreground?

当一个应用程序位于另一个应用程序之后
我单击应用程序的任务栏图标,我希望整个应用程序能够
即使是应用程序模式的WS_POPUP对话框位于z顺序的顶部
打开。

但是,有时候,对于我的某些对话框(和其他对话框),只有该对话框位于最前面;其余的应用程序则留在后面。

我看过Spy ++,对于可以正常工作的那些,我可以看到
WM_WINDOWPOSCHANGING被发送到对话框的父级。对于那些
将其余的应用程序留下,WM_WINDOWPOSCHANGING不在
发送给对话框的父级。

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

简而言之,都是使用DialogBoxParam()创建的WS_POPUPWINDOW窗口,
传递了与第三个参数相同的HWND。

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

在我的情况下,基本的父母身份是一个MDI框架……这是否以某种方式起作用?


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

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

这既适用于模态窗口,如对话框和消息框,也适用于无模态窗口。设置无模式弹出窗口的所有者还可以使弹出窗口始终保持在其所有者之上。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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中,可以指定所有者:

1
2
3
4
5
6
7
8
public DialogResult ShowDialog(
    IWin32Window owner
)

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

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

1
2
3
public void Show(
    IWin32Window owner,
)

或等效地:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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
);

或之后:

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

或(64位兼容)

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

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

Do not call SetWindowLongPtr with the GWLP_HWNDPARENT index to change the parent of a child window. Instead, use the SetParent function.

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


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

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


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

发布此内容后,我启动了自己的Windows Forms应用程序,并重现了您描述的问题。我有两个对话框,一个正确地工作,另一个没有,并且我看不到任何直接原因就是它们的行为不同。如果发现的话,我会更新的。

雷蒙·陈你在哪里!


推荐阅读