「如何用 VC++ 設定桌面牆紙?」是論壇高頻提問。舊 API SystemParametersInfo 僅支援 BMP;若要支援 JPG/PNG 等現代格式,必須啟用 **Active Desktop** 介面 IActiveDesktop。本文雙軌並行,給出最完整落地代碼。
BOOL SetWallpaperBMP(LPCTSTR pszBmpPath) {
return SystemParametersInfo(
SPI_SETDESKWALLPAPER, 0,
(PVOID)pszBmpPath,
SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE
);
}
// 缺點:不支援 JPG/PNG、無法設定填充/拉伸/置中樣式 (需改註冊表並重啟 explorer)
#include <shlobj.h>、#include <wininet.h> (順序敏感,置於 afxext.h 之後)#pragma comment(lib, "wininet.lib")
BOOL SetWallpaperEx(LPCTSTR pszImagePath, DWORD dwStyle = WPSTYLE_CENTER)
{
// 1. 建立 IActiveDesktop 介面
IActiveDesktop* pAD = NULL;
HRESULT hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveDesktop, (void**)&pAD);
if (FAILED(hr)) return FALSE;
// 2. 啟用 Active Desktop (必要步驟,否則 SetWallpaper 失效)
COMPONENTSOPT co = { sizeof(COMPONENTSOPT) };
co.fEnableComponents = TRUE;
co.fActiveDesktop = TRUE;
hr = pAD->SetDesktopItemOptions(&co, 0);
if (FAILED(hr)) { pAD->Release(); return FALSE; }
// 3. 字串轉寬字元 (SetWallpaper 參數為 LPCWSTR)
WCHAR wszPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszImagePath, -1, wszPath, MAX_PATH);
// 4. 設定牆紙路徑
hr = pAD->SetWallpaper(wszPath, 0);
if (FAILED(hr)) { pAD->Release(); return FALSE; }
// 5. 設定顯示樣式
WALLPAPEROPT wo = { sizeof(WALLPAPEROPT) };
wo.dwStyle = dwStyle; // WPSTYLE_CENTER | WPSTYLE_TILE | WPSTYLE_STRETCH | WPSTYLE_MAX (Win7+)
hr = pAD->SetWallpaperOptions(&wo, 0);
if (FAILED(hr)) { pAD->Release(); return FALSE; }
// 6. 套用變更
hr = pAD->ApplyChanges(AD_APPLY_ALL);
pAD->Release();
return SUCCEEDED(hr);
}
// 使用範例
SetWallpaperEx(_T("C:\\Pictures\\photo.jpg"), WPSTYLE_STRETCH); // 拉伸填滿
SetWallpaperEx(_T("C:\\Pictures\\logo.png"), WPSTYLE_CENTER); // 置中
| 旗標 | 效果 | 備註 |
|---|---|---|
WPSTYLE_CENTER | 置中,不縮放 | 預設 |
WPSTYLE_TILE | 平鋪重複 | |
WPSTYLE_STRETCH | 拉伸填滿 (可能變形) | |
WPSTYLE_MAX | 縮放填滿保持比例 (Win7+) | 「填滿」模式 |
WPSTYLE_MIN | 縮放適應保持比例 (Win7+) | 「適應」模式 |
void CRotateWallpaperDlg::OnTimer(UINT_PTR nIDEvent) {
static int idx = 0;
CStringArray paths;
paths.Add(_T("C:\\Wallpaper\\1.jpg"));
paths.Add(_T("C:\\Wallpaper\\2.png"));
paths.Add(_T("C:\\Wallpaper\\3.bmp"));
SetWallpaperEx(paths[idx % paths.GetCount()], WPSTYLE_STRETCH);
idx++;
CDialog::OnTimer(nIDEvent);
}
| 現象 | 原因 | 對策 |
|---|---|---|
| SetWallpaper 返回 0x80004005 (E_FAIL) | Active Desktop 未啟用 | 務必先呼叫 SetDesktopItemOptions 設 fActiveDesktop=TRUE |
| JPG/PNG 顯示為黑色/空白 | 路徑編碼錯誤 (ANSI/Unicode 不匹配) | 統一用 MultiByteToWideChar 或專案設定 Unicode 字元集 |
| 套用後桌面圖示閃爍/重排 | ApplyChanges 觸發 explorer 重繪 | 正常現象;若頻繁切換可考慮直接操作註冊表 Wallpaper 值 + SPIF_SENDWININICHANGE |
| 多顯示器不同牆紙 | IActiveDesktop 僅支援單一牆紙 | Win10+ 需用 IDesktopWallpaper (VirtualDesktop API) |
#include
#pragma comment(lib, "shell32.lib")
void SetWallpaperModern(LPCWSTR pszPath, DESKTOP_WALLPAPER_POSITION pos = DWPOS_CENTER) {
IDesktopWallpaper* pDW = NULL;
CoCreateInstance(CLSID_DesktopWallpaper, NULL, CLSCTX_INPROC_SERVER,
IID_IDesktopWallpaper, (void**)&pDW);
if (pDW) {
pDW->SetWallpaper(NULL, pszPath); // NULL = 所有顯示器
pDW->SetPosition(pos);
pDW->Release();
}
}
從 SystemParametersInfo 的 BMP 時代,到 IActiveDesktop 的全格式支援,再到 IDesktopWallpaper 的多螢幕原生體驗,Windows 牆紙 API 見證了桌面演進。**核心心法**:(1) 先啟用 Active Desktop;(2) 路徑務必轉寬字元;(3) 別忘了 ApplyChanges。下次需求來時,直接複製 SetWallpaperEx,三分鐘交付,剩下時間喝咖啡。