为什么隐藏Owned窗口时,Owner窗口会被后置

最近在开发的时候遇到一个怪异的问题,问题背景:在程序中,有一个Owner窗口拥有多个Owned窗口,当关闭Owned窗口时,会调用ShowWindow来隐藏该窗口,而不是真正的关闭。当最后一个Owned窗口隐藏时,理论上应该要激活Owner窗口,然而实际上激活的是位于Owner窗口下面的另外一个窗口。

经过一番检查,最终发现问题的根源在于Owned窗口的样式:如果Owned窗口具有WS_OVERLAPPED样式,就会出现该问题;而把样式改成WS_POPUP之后,问题就会消失。

可以用以下的代码来试验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "Windows.h"

int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, LPWSTR, int) {

//注册Owner窗口类
//...

//创建Owner窗口
HWND owner_window = CreateWindow(
L"OwnerWindowClass",
L"Owner",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
instance,
nullptr);

//注册Owned窗口类,使用OwnedWindowProcedure作为窗口过程函数
//...

//创建第一个Owned窗口
HWND owned_window1 = CreateWindow(
L"OwnedWindowClass",
L"Owned",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
0,
0,
500,
500,
owner_window,
nullptr,
instance,
nullptr);

//创建第二个Owned窗口
HWND owned_window2 = CreateWindow(
L"OwnedWindowClass",
L"Owned",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
0,
0,
500,
500,
owner_window,
nullptr,
instance,
nullptr);

MSG message{};
while (GetMessage(&message, nullptr, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}

return 0;
}

LRESULT CALLBACK OwnedWindowProcedure(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam) {

//拦截关闭行为,改成隐藏
if (message == WM_CLOSE) {
ShowWindow(window_handle, SW_HIDE);
return 0;
}

return DefWindowProc(window_handle, message, wparam, lparam);
}

运行以上代码,会显示一个Owner窗口和两个Owned窗口。逐个关闭Owned窗口,在关闭最后一个窗口时,如果桌面上存在其它窗口,那么必然会激活其它窗口,而不是Owner窗口。把Owned窗口的样式改成WS_POPUP之后,就不会再出现这个现象了。

从这个问题可以看出,Overlapped窗口和Popup窗口并不仅仅是默认有无边框的区别,在一些细节行为上,这两种窗口会有不同的表现。一般来说,WS_OVERLAPPED样式用于顶层的Owner窗口,而Owned窗口应尽量使用WS_POPUP样式,这样可以避免出现一些意料之外的表现。