在Windows系统中,在按下Win+D快捷键等操作执行“显示桌面”时,会将桌面上的所有窗口都最小化到任务栏,再次按下还原窗口。在显示桌面时,有些窗口仍然保持显示状态,不被最小话,即称之为逃脱显示桌面指令。

实现逃脱显示桌面的方式有三种,每种方式有不同的应用场景。

一、置顶窗口的方式

首先创建 WS_EX_TOOLWINDOW 风格的窗口,WS_EX_TOOLWINDOW风格的窗口不会出现在任务栏,而且按ALT+TAB时也不会显示,然后为该窗口设置WS_EX_TOPMOST样式,使其始终置顶。

可以在创建窗口时通过扩展样式来指定WS_EX_TOOLWINDOW和WS_EX_TOPMOST样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
HWND hWnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
nullptr,
nullptr,
hInstance,
nullptr);

也可以先创建窗口,然后修改窗口样式:

1
2
3
4
LONG_PTR oldStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
oldStyle |= WS_EX_TOOLWINDOW;

SetWindowLongPtr(hWnd, GWL_EXSTYLE, oldStyle);

并通过SetWindowPos函数来置顶窗口:

1
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

这种方式对系统的兼容性比较好,经测试可以支持Windows XP/7/8/10/11系统。但这种方式会导致窗口一直置顶显示,对于不需要置顶显示的窗口,这种方式就不适用了。

二、改变窗口所有者的方式

在执行显示桌面指令时,桌面仍然会显示,因此我们可以将窗口的所有者设置为桌面窗口,来确保窗口不受“显示桌面”指令的影响。

2.1 查找桌面窗口

在Win7及之前的系统中,桌面窗口是Program Manager窗口的子窗口,如下图所示:

而在Win8及以后的系统,桌面窗口变成了WorkerW窗口的子窗口,如下图所示:

因此在查找桌面窗口句柄时,需要根据不同的系统环境采用不同的查找方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HWND FindShellDefViewWnd() {
HWND hShellDefView = nullptr;

// Try Win7 and later
HWND hProgmanWnd = FindWindowW(L"Progman", L"Program Manager");
if (hProgmanWnd) {
hShellDefView = FindWindowExW(hProgmanWnd, NULL, L"SHELLDLL_DefView", NULL);
}

// Try > Win7
if (!hShellDefView) {
// When this fails, then look for the WorkerW windows list to get the correct desktop list handle.
HWND hDesktop = GetDesktopWindow();
HWND hWorkerW = NULL;
do {
hWorkerW = FindWindowExW(hDesktop, hWorkerW, L"WorkerW", NULL);
hShellDefView = FindWindowExW(hWorkerW, NULL, L"SHELLDLL_DefView", NULL);
} while (hShellDefView == NULL && hWorkerW != NULL);
}
return hShellDefView;
}

2.2 设置窗口拥有者

现在我们将窗口的拥有者设置为SHELLDLL_DefView窗口即可逃脱显示桌面的指令了。

1
2
3
4
5
6
7
8
bool EscapeShowDesktop(HWND hWnd) {
HWND hShellDefView = FindShellDefViewWnd();
if (hShellDefView) {
SetWindowLongPtr(hWnd, GWL_HWNDPARENT, (LONG_PTR)hShellDefView);
return true;
}
return false;
}

需要注意:

采用这种方式可能会导致当前的活动桌面发生改变,这个行为会对某些Windows API产生影响,如会导致GetAsyncKeyState失效。

三、使用Appbar机制实现

Windows提供了API来使应用程序具有模拟系统任务栏的能力,通常将模拟系统任务栏的程序成为Appbar(application desktop toolbar)。
Appbar窗口不受显示桌面指令的控制,但该窗口会和任务栏一样占据一部分桌面可用空间,可以通过API控制占用空间的大小和位置。

关于如何使用Appbar的API,可以参考Windows官方文档:
Using Application Desktop Toolbars