傳統 SystemParametersInfo(SPI_SETDESKWALLPAPER, ...) 僅支援 BMP 格式,需自行轉檔。Windows 98/2000 引入 Active Desktop 技術,提供 IActiveDesktop COM 介面,原生支援 **JPG、PNG、GIF、HTML** 等格式直接設為壁紙,並可啟用/停用 Active Desktop、設定壁紙樣式 (置中、平鋪、拉伸)。本文提供完整 C++ Builder 實作。
#define NO_WIN32_LEAN_AND_MEAN (在專案最早 include 前,避免 windows.h 衝突)#include (包含 IActiveDesktop, CLSID_ActiveDesktop, IID_IActiveDesktop 定義)oleaut32.lib, uuid.lib (BCB 專案通常自動處理)
#include
#include // _bstr_t, _com_error
#include // OleCheck
// 設定桌面壁紙 (支援 JPG, PNG, GIF, BMP, HTML)
// wallpaperPath: 完整檔案路徑 (本機或 UNC 網路路徑)
// style: WPSTYLE_CENTER (0), WPSTYLE_TILE (1), WPSTYLE_STRETCH (2), WPSTYLE_FIT (3), WPSTYLE_FILL (4) - Vista+
// 回傳: true 成功, false 失敗 (可用 GetLastError 或 _com_error 取得細節)
bool SetWallpaperEx(const wchar_t* wallpaperPath, DWORD style = WPSTYLE_STRETCH) {
// 1. 初始化 COM 單元執行緒 (STA)
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr)) return false; // 已初始化則傳回 S_FALSE 視為成功
IActiveDesktop* pActiveDesktop = NULL;
bool success = false;
try {
// 2. 建立 Active Desktop 物件
hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveDesktop, (void**)&pActiveDesktop);
if (FAILED(hr)) throw _com_error(hr);
// 3. 設定壁紙路徑 (需 BSTR / WideString)
_bstr_t bstrPath(wallpaperPath); // 自動管理 BSTR 生命週期
hr = pActiveDesktop->SetWallpaper(bstrPath, 0); // 0 = 保留未來旗標
if (FAILED(hr)) throw _com_error(hr);
// 4. 設定壁紙樣式 (選用,需先取得選項再修改)
WALLPAPEROPT wpo = { sizeof(WALLPAPEROPT) };
hr = pActiveDesktop->GetWallpaperOptions(&wpo, 0);
if (SUCCEEDED(hr)) {
wpo.dwStyle = style; // WPSTYLE_*
hr = pActiveDesktop->SetWallpaperOptions(&wpo, 0);
if (FAILED(hr)) throw _com_error(hr);
}
// 5. 套用變更 (關鍵步驟!)
hr = pActiveDesktop->ApplyChanges(AD_APPLY_ALL); // AD_APPLY_SAVE | AD_APPLY_HTMLGEN | AD_APPLY_REFRESH | AD_APPLY_ALL
if (FAILED(hr)) throw _com_error(hr);
success = true;
}
catch (const _com_error& e) {
// 可記錄 e.Error(), e.ErrorMessage(), e.Description()
success = false;
}
// 6. 釋放介面與反初始化 COM
if (pActiveDesktop) pActiveDesktop->Release();
CoUninitialize();
return success;
}
// Unit1.cpp
#include
#pragma hdrstop
#include "Unit1.h"
#include
#include
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {}
void __fastcall TForm1::BtnSetWallpaperClick(TObject *Sender) {
// 開啟檔案對話框選擇圖片
TOpenDialog* dlg = new TOpenDialog(this);
dlg->Filter = "圖片檔案 (*.jpg;*.jpeg;*.png;*.bmp;*.gif)|*.jpg;*.jpeg;*.png;*.bmp;*.gif|所有檔案 (*.*)|*.*";
dlg->Options << ofFileMustExist << ofPathMustExist;
if (!dlg->Execute()) { delete dlg; return; }
AnsiString file = dlg->FileName;
delete dlg;
// 對應 ComboBox 選擇的樣式
DWORD style = WPSTYLE_STRETCH; // 預設拉伸
switch (ComboBoxStyle->ItemIndex) {
case 0: style = WPSTYLE_CENTER; break; // 置中
case 1: style = WPSTYLE_TILE; break; // 平鋪
case 2: style = WPSTYLE_STRETCH; break; // 拉伸
case 3: style = WPSTYLE_FIT; break; // 適應 (Vista+)
case 4: style = WPSTYLE_FILL; break; // 填滿 (Vista+)
}
Screen->Cursor = crHourGlass;
try {
if (SetWallpaperEx(file.c_str(), style))
ShowMessage("壁紙設定成功!");
else
ShowMessage("壁紙設定失敗,請確認檔案存在且具備權限。");
}
__finally {
Screen->Cursor = crDefault;
}
}
| 常數 | 值 | 說明 | 支援版本 |
|---|---|---|---|
| WPSTYLE_CENTER | 0 | 置中,保持原始尺寸 | All |
| WPSTYLE_TILE | 1 | 平鋪重複 | All |
| WPSTYLE_STRETCH | 2 | 拉伸填滿螢幕 (可能變形) | All |
| WPSTYLE_FIT | 3 | 等比縮放至完全顯示 (可能有邊框) | Vista+ |
| WPSTYLE_FILL | 4 | 等比縮放至填滿螢幕 (可能裁切) | Vista+ |
| WPSTYLE_SPAN | 5 | 跨多螢幕延展 (Win8+) | Win8+ |
| 問題 | 原因 | 解決 |
|---|---|---|
| CoCreateInstance 回傳 0x80040154 (CLASS_E_CLASSNOTAVAILABLE) | Active Desktop 元件未註冊/損毀 (WinXP SP2+ 預設停用 Active Desktop) | 執行 regsvr32 shdocvw.dll、regsvr32 browseui.dll;或改用 IActiveDesktop 替代方案 (見下) |
| ApplyChanges 失敗 / 壁紙不更新 | 權限不足、群組原則禁用 Active Desktop、桌面視窗未就緒 | 檢查 HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\ActiveDesktop;嘗試 SystemParametersInfo(SPI_SETDESKWALLPAPER) 備援 |
| HTML 壁紙不顯示/腳本錯誤 | Active Desktop 使用 MSHTML (IE 引擎),現代網頁標準不支援 | **不建議**使用 HTML 壁紙,僅用於相容舊系統 |
| 64 位元專案編譯錯誤 | shlobj.h 定義衝突、COM 介面指標大小 | 確認 _WIN32_DCOM 定義、使用 IActiveDesktop* 而非 IActiveDesktopPtr (智慧指標需正確匯入型別庫) |
Active Desktop 在 Vista 後被 **Desktop Window Manager (DWM)** 取代,IActiveDesktop 仍相容但屬舊技術。現代建議:
#include // IDesktopWallpaper, CLSID_DesktopWallpaper
bool SetWallpaperModern(const wchar_t* path, DESKTOP_WALLPAPER_POSITION pos = DWPOS_FILL) {
IDesktopWallpaper* pWallpaper = NULL;
HRESULT hr = CoCreateInstance(CLSID_DesktopWallpaper, NULL, CLSCTX_INPROC_SERVER,
IID_IDesktopWallpaper, (void**)&pWallpaper);
if (SUCCEEDED(hr)) {
hr = pWallpaper->SetWallpaper(NULL, path); // NULL = 所有顯示器
if (SUCCEEDED(hr)) hr = pWallpaper->SetPosition(pos);
pWallpaper->Release();
}
return SUCCEEDED(hr);
}
優點:原生支援多顯示器獨立壁紙、高 DPI、動態換圖 (SlideShow)、無 Active Desktop 相依。
bool SetWallpaperCompat(const wchar_t* path) {
// 1. 使用 GDI+/WIC 將 JPG/PNG 載入並儲存為暫存 BMP
// 2. SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (PVOID)bmpPath, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
// 3. 刪除暫存 BMP
// 適合不想引入 COM 相依、需支援極舊系統 (Win95/NT4) 的專案
}
IActiveDesktop 曾是 Windows 98/2000/XP 時代設定非 BMP 壁紙的標準方式。雖然現代系統已轉向 IDesktopWallpaper,但理解 IActiveDesktop 的 COM 介面操作流程 (建立物件、設定屬性、ApplyChanges 套用) 仍具教學價值。實際專案建議**優先使用 IDesktopWallpaper (Win7+)**,舊系統相容才回退至 IActiveDesktop 或 SystemParametersInfo + 圖片轉檔方案。