解决Windows 11 24H2 专业版 Dev 渠道 Build 26120.670版本升级后,壁纸无法正常显示问题
解决Windows 11 24H2 专业版 Dev 渠道 Build 26120.670(Feature Experience Pack 1000.26100.6.0)和 24H2 Canary 渠道(Build 26080)版本升级后,壁纸无法正常显示问题
windows于2025年6月更新后,窗口显示方式发生变化,以兼容windows新的特性,进而导致壁纸使用原方法嵌入并显示,以下是微软概述,来自于https://blog.csdn.net/qq_59075481/article/details/125361650#comments_37448067
从微软获得此信息:
随着 Windows 不断发展以帮助提供最佳客户体验,我们改变了 Windows 中的背景呈现方式,以支持新场景,例如 Windows Insider 中宣布的 HDR 背景。
当桌面从列表视图窗口(ListView window)分离出来时,我们不再创建多个顶级窗口(HWND,此前是两个 WorkerW HWND)来支持此场景。取而代之的是,“Progman”窗口作为顶级窗口,现在使用 WS_EX_NOREDIRECTIONBITMAP 创建,因此该窗口根本没有 GDI 内容,并且 Shell DefView 子窗口是一个具有 WS_EX_LAYERED 样式的子窗口(也就是分层窗口)。当桌面升起时,我们会在 DefView 下方创建一个按 Z 轴顺序排列的 WorkerW 子窗口,用于渲染壁纸。DefView 窗口将几乎完全透明地显示,仅包含图标和文本。
如果您的应用程序强制启用“RaisedDesktop”状态,则它现在需要创建自己的 WS_EX_LAYERED 子窗口,并拥有一个 HWND 句柄,该 HWND 的 Z 轴顺序位于 DefView 窗口下方,且位于 WorkerW 窗口上方。此窗口最好是一个 SetLayeredWindowAttributes(bAlpha=0xFF) 窗口,以便您可以向其进行 DX Blt 呈现,而不会出现性能问题。
新旧差异说明
旧版桌面发送 0x052C 消息后,桌面窗体层次为
而更新前窗体层次为
基于此说明,嵌入壁纸方案可用如下代码
1、发送 0x052C消息。
2、找到Program Manager窗体句柄。
3、找到SHELLDLL_DefView与WrokerW句柄,用于后续窗体Z序调整。
4、将壁纸窗体句柄设置为Program Manager窗体句柄的子窗口。
5、调整窗体顺序为SHELLDLL_DefView 壁纸 WrokerW的顺序,如下
HWND playBackGroundVideo(std::wstring videoPathW, bool loop, bool* bIsVersion1_2, HANDLE* ffplayProcessHandle, DWORD* ffplayProcessId) {
// 24H2
HWND hTopDeskWnd = FindWindowW(L"Progman", L"Program Manager");
if (!hTopDeskWnd)
{
std::cerr << "Not found desktop top parent window!\n";
return NULL;
}
if (!RaiseDesktop(hTopDeskWnd))
{
std::cerr << "Failed to raise desktop!\n";
return NULL;
}
HWND hShellDefView = FindWindowExW(hTopDeskWnd, 0, L"SHELLDLL_DefView", L"");
HWND hWorker1 = nullptr, hWorker2 = nullptr;
if (!hShellDefView)
{ // 如果没有找到,则回退 23H2 的搜索模式
HWND hWorker_p1 = GetWindow(hTopDeskWnd, GW_HWNDPREV);
if (hWorker_p1)
{
hShellDefView = FindWindowExW(hWorker_p1, 0, L"SHELLDLL_DefView", L"");
if (!hShellDefView)
{
hWorker2 = hWorker_p1;
HWND hWorker_p2 = GetWindow(hWorker_p1, GW_HWNDPREV);
if (hWorker_p1)
{
hShellDefView = FindWindowExW(hWorker_p2, 0, L"SHELLDLL_DefView", L"");
if (!hShellDefView)
{
std::cerr << "Not found desktop shell defview window!\n";
return NULL;
}
else {
hWorker1 = hWorker_p2;
}
}
}
else {
hWorker1 = hWorker_p1;
}
}
}
HWND hWorker = FindWindowExW(hTopDeskWnd, 0, L"WorkerW", L"");
if (!hWorker)
{
hWorker = FindWindowExW(hTopDeskWnd, 0, L"WorkerA", L"");
if (!hWorker)
{
std::cerr << "Not found system wallpaper worker window(23H2?)\n";
/*system("pause");
return NULL;*/
}
}
// 23H2
*bIsVersion1_2 = false;
if (!hWorker) {
hWorker = !hWorker2 ? hTopDeskWnd : hWorker2;
*bIsVersion1_2 = true;
}
if ((!*bIsVersion1_2 || !hWorker) && !hTopDeskWnd) {
return NULL;
}
HWND hEmbedWnd = nullptr/* 要嵌入桌面的窗口句柄 */;
LONG_PTR style_tw = GetWindowLongPtrW(hEmbedWnd, GWL_STYLE);
LONG_PTR exstyle_tw = GetWindowLongPtrW(hEmbedWnd, GWL_EXSTYLE);
if (!(exstyle_tw & WS_EX_LAYERED))
{
exstyle_tw |= WS_EX_LAYERED;
}
if ((exstyle_tw & WS_EX_TOOLWINDOW))
{
exstyle_tw &= ~WS_EX_TOOLWINDOW;
}
if ((style_tw & WS_CHILDWINDOW))
{
style_tw &= ~WS_CHILDWINDOW;
}
if ((style_tw & WS_OVERLAPPED))
{
style_tw &= ~WS_OVERLAPPED;
}
if ((style_tw & WS_CAPTION))
{
style_tw &= ~WS_CAPTION;
}
if ((style_tw & WS_BORDER))
{
style_tw &= ~WS_BORDER;
}
if ((style_tw & WS_SYSMENU))
{
style_tw &= ~WS_SYSMENU;
}
if ((style_tw & WS_THICKFRAME))
{
style_tw &= ~WS_THICKFRAME;
}
SetWindowLongPtrW(hEmbedWnd, GWL_STYLE, style_tw);
SetWindowLongPtrW(hEmbedWnd, GWL_EXSTYLE, exstyle_tw);
if (!loop) {
SetLayeredWindowAttributes(hEmbedWnd, 0, 0x00, LWA_ALPHA);
}
SetParent(hEmbedWnd, *bIsVersion1_2 ? hWorker : hTopDeskWnd);
RECT rchTestOld{};
GetWindowRect(hEmbedWnd, &rchTestOld);
int rcx = GetSystemMetrics(SM_CXSCREEN);
int rcy = GetSystemMetrics(SM_CYSCREEN);
cout << rcx << rcy << "\n";
if (rcx <= 0 || rcy <= 0)
{
std::cerr << "get system metrics failed with error!\n";
system("pause");
return NULL;
}
RECT rcFullScreen{ 0, 0, (LONG)rcx, (LONG)rcy };
AdjustWindowRect(&rcFullScreen, style_tw, FALSE);
int rcfx = rcFullScreen.right - rcFullScreen.left;
int rcfy = rcFullScreen.bottom - rcFullScreen.top;
printfWithTime("移动窗口:%d,%d,%d,%d\n", rcFullScreen.left, rcFullScreen.top, rcfx, rcfy);
BOOL res = MoveWindow(hEmbedWnd, rcFullScreen.left, rcFullScreen.top, -rcfx, -rcfy, TRUE);
if (!res)
{
DWORD err = GetLastError();
// 输出错误信息
//MessageBox(NULL, err, L"Wallpaper And Icons", MB_ICONERROR);
}
WINDOWPLACEMENT wp{};
wp.length = sizeof(WINDOWPLACEMENT);
wp.flags = WPF_SETMINPOSITION;
if (loop)
wp.showCmd = SW_SHOWNORMAL;
wp.ptMinPosition = { rcFullScreen.left, rcFullScreen.top };
wp.ptMaxPosition = { rcFullScreen.left, rcFullScreen.top };
wp.rcNormalPosition = rcFullScreen;
SetWindowPlacement(hEmbedWnd, &wp);
SetWindowPos(hEmbedWnd, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE
| SWP_NOACTIVATE | SWP_DRAWFRAME);
SetWindowPos(hShellDefView, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE
| SWP_NOACTIVATE);
SetWindowPos(hWorker, HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE
| SWP_NOACTIVATE | SWP_DRAWFRAME);
return hEmbedWnd;
}
可能存在的问题
对于像MFC这样的GDI应用程序,在上述代码中壁纸可能无法正常显示,需要将MoveWindow函数中的窗口大小调整为一个中间值,然后使用SetWindowPlacement绘制成最终窗体大小,从而触发windows的刷新,壁纸正常显示
BOOL res = MoveWindow(hEmbedWnd, rcFullScreen.left, rcFullScreen.top, -rcfx, -rcfy, TRUE);