應用程序需要動態設置桌面壁紙(如每日必應圖片、用戶上傳圖片輪播),且需支持 JPG/PNG/BMP 多格式、居中/平鋪/拉伸多模式。
自 Windows 2000 起,Shell 提供 IActiveDesktop 接口(CLSID: CLSID_ActiveDesktop),比傳統 SystemParametersInfo(SPI_SETDESKWALLPAPER) 更強大:
#define NO_WIN32_LEAN_AND_MEAN // 必須在包含windows.h之前定義
#include // IActiveDesktop 定義
#include // CComPtr 智能指針
HRESULT SetWallpaper(const wchar_t* imagePath, DWORD style = 0) {
// 1. 初始化 COM 庫
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) return hr;
// 2. 創建 ActiveDesktop 實例
CComPtr pActiveDesktop;
hr = pActiveDesktop.CoCreateInstance(CLSID_ActiveDesktop);
if (FAILED(hr)) {
CoUninitialize();
return hr;
}
// 3. 設置壁紙路徑(支持本地文件、UNC路徑、HTTP/HTTPS URL)
hr = pActiveDesktop->SetWallpaper(imagePath, 0);
if (FAILED(hr)) {
CoUninitialize();
return hr;
}
// 4. 設置顯示樣式
// WPSTYLE_CENTER (0), WPSTYLE_TILE (1), WPSTYLE_STRETCH (2), WPSTYLE_FIT (3), WPSTYLE_FILL (4), WPSTYLE_SPAN (5)
COMPONENTSOPT compOpt = { sizeof(COMPONENTSOPT) };
hr = pActiveDesktop->GetDesktopItemOptions(&compOpt, 0);
if (SUCCEEDED(hr)) {
compOpt.dwSize = sizeof(COMPONENTSOPT);
compOpt.dwFlags = COMP_ELEM_TYPE | COMP_ELEM_CHECKED | COMP_ELEM_POSITION;
// 這裡簡化處理,實際需根據style設置WPSTYLE_*常量
hr = pActiveDesktop->SetDesktopItemOptions(&compOpt, 0);
}
// 5. 應用變更(AD_APPLY_ALL: 所有用戶/所有顯示器)
if (SUCCEEDED(hr)) {
hr = pActiveDesktop->ApplyChanges(AD_APPLY_ALL);
}
CoUninitialize();
return hr;
}
| 步驟 | 關鍵點 |
|---|---|
| COM 初始化 | 必須在線程中調用 CoInitializeEx,匹配 CoUninitialize |
| 實例創建 | 使用 CLSID_ActiveDesktop,需包含 shlobj.h 並鏈接 shell32.lib |
| 路徑格式 | 本地路徑:C:\\Wallpaper.jpg;網絡圖片:https://example.com/bg.jpg(自動下載緩存) |
| 樣式常量 | WPSTYLE_CENTER=0、WPSTYLE_TILE=1、WPSTYLE_STRETCH=2、WPSTYLE_FIT=3(Win7+)、WPSTYLE_FILL=4(Win7+)、WPSTYLE_SPAN=5(Win8+多顯示器跨屏) |
| 應用變更 | AD_APPLY_ALL(所有用戶/顯示器)、AD_APPLY_SAVE(僅保存不立即應用)、AD_APPLY_HTMLGEN(生成HTML壁紙) |
| 錯誤處理 | 檢查每步 HRESULT,常見失敗:路徑不存在、格式不支持、權限不足、COM未初始化 |
using System;
using System.Runtime.InteropServices;
public static class WallpaperManager {
[ComImport, Guid("F490EB00-1240-11D1-9888-006097DEACF9")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IActiveDesktop {
[PreserveSig] int ApplyChanges(uint dwFlags);
[PreserveSig] int GetWallpaper([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszWallpaper, int cchWallpaper, uint dwReserved);
[PreserveSig] int SetWallpaper([In, MarshalAs(UnmanagedType.LPWStr)] string pwszWallpaper, uint dwReserved);
[PreserveSig] int GetWallpaperOptions(ref WALLPAPEROPT pwpo, uint dwReserved);
[PreserveSig] int SetWallpaperOptions(ref WALLPAPEROPT pwpo, uint dwReserved);
// ... 其他成員
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct WALLPAPEROPT {
public int dwSize;
public int dwStyle; // WPSTYLE_*
}
const uint AD_APPLY_ALL = 0x00000001;
const int CLSCTX_INPROC_SERVER = 0x1;
const int S_OK = 0;
[DllImport("shell32.dll")]
static extern int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter, uint dwClsContext, ref Guid riid, out IActiveDesktop ppv);
public static void SetWallpaper(string path, int style = 0) {
Guid clsid = new Guid("75048700-EF1F-11D0-9888-006097DEACF9"); // CLSID_ActiveDesktop
Guid iid = typeof(IActiveDesktop).GUID;
if (CoCreateInstance(ref clsid, IntPtr.Zero, CLSCTX_INPROC_SERVER, ref iid, out var desktop) == S_OK) {
desktop.SetWallpaper(path, 0);
var opt = new WALLPAPEROPT { dwSize = Marshal.SizeOf(), dwStyle = style };
desktop.SetWallpaperOptions(ref opt, 0);
desktop.ApplyChanges(AD_APPLY_ALL);
Marshal.ReleaseComObject(desktop);
}
}
}
WTSQueryUserToken + CreateProcessAsUser 注入用戶會話IDesktopWallpaper 接口(SetWallpaper(monitorID, path))Windows 8+ 推薦使用 IDesktopWallpaper 接口(shobjidl.h),提供:
但 IActiveDesktop 仍是 XP/Win7 兼容的最佳選擇,兩者可根據運行時版本動態選擇。