解决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);

文章作者: Lulu6432
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Lulu6432技术漫步
硬核知识点 知识
喜欢就支持一下吧