首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 呼叫 SystemParametersInfo API 更換桌面壁紙完整實作

     編輯:關於C語言

前言

Windows 系統參數資訊函式 SystemParametersInfo 可讀寫多種系統級設定,其中 SPI_SETDESKWALLPAPER (0x0014) 可程式化更換桌面壁紙。本文完整示範 C# P/Invoke 宣告、圖片格式轉換 (僅支援 BMP)、檔案選擇 UI、預覽與套用流程。

API 簽名與常數

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SystemParametersInfo(
    uint uiAction,
    uint uiParam,
    string pvParam,
    uint fWinIni);

// 常數
public const uint SPI_SETDESKWALLPAPER = 0x0014;
public const uint SPIF_UPDATEINIFILE = 0x01;   // 寫入 user profile
public const uint SPIF_SENDCHANGE = 0x02;      // 廣播 WM_SETTINGCHANGE 通知其他程式
  • CharSet.Auto:自動依專案 Unicode/ANSI 封送字串路徑。
  • SetLastError=true:失敗時可呼叫 Marshal.GetLastWin32Error() 取得錯誤碼。
  • fWinIni 組合 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE 確保即時生效並持久化。

完整表單程式碼

public partial class WallpaperForm : Form
{
    private string _selectedBmpPath;

    public WallpaperForm()
    {
        InitializeComponent();
        // UI: btnSelect, btnApply, txtPath, picPreview, openFileDialog1
    }

    private void btnSelect_Click(object sender, EventArgs e)
    {
        openFileDialog1.Filter = "圖片檔案|*.bmp;*.jpg;*.jpeg;*.png;*.gif|所有檔案|*.*";
        openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            string srcPath = openFileDialog1.FileName;
            txtPath.Text = srcPath;

            try
            {
                using (var img = Image.FromFile(srcPath))
                {
                    picPreview.Image = new Bitmap(img); // 複製避免檔案鎖定

                    // 必須轉為 BMP 才能設定壁紙
                    string ext = Path.GetExtension(srcPath).ToLowerInvariant();
                    if (ext != ".bmp")
                    {
                        _selectedBmpPath = Path.ChangeExtension(Path.GetTempFileName(), ".bmp");
                        img.Save(_selectedBmpPath, ImageFormat.Bmp);
                    }
                    else
                    {
                        _selectedBmpPath = srcPath;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"圖片載入失敗: {ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }

    private void btnApply_Click(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(_selectedBmpPath) || !File.Exists(_selectedBmpPath))
        {
            MessageBox.Show("請先選擇有效圖片", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        bool result = SystemParametersInfo(
            SPI_SETDESKWALLPAPER,
            0,
            _selectedBmpPath,
            SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);

        if (result)
        {
            MessageBox.Show("桌面壁紙已更新", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        else
        {
            int err = Marshal.GetLastWin32Error();
            MessageBox.Show($"更新失敗 (錯誤碼: {err})", "失敗", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // 清理暫存 BMP
        if (!string.IsNullOrEmpty(_selectedBmpPath) &&
            _selectedBmpPath != txtPath.Text && // 非原始 BMP
            File.Exists(_selectedBmpPath))
        {
            try { File.Delete(_selectedBmpPath); } catch { /* 忽略 */ }
        }
        base.OnFormClosing(e);
    }
}

關鍵技術細節

為何必須轉 BMP?

SystemParametersInfo(SPI_SETDESKWALLPAPER) 僅接受 BMP 格式路徑。JPG/PNG 直接傳遞會失敗 (錯誤碼 87 - 參數錯誤) 或顯示黑畫面。故需先轉檔。

圖片轉換最佳實務

// 使用 Image.Save 直接轉 BMP (無損但檔案較大)
image.Save(bmpPath, ImageFormat.Bmp);

// 若需壓縮可先縮放至螢幕解析度再存 BMP
var screenSize = Screen.PrimaryScreen.Bounds.Size;
using var resized = new Bitmap(image, screenSize);
resized.Save(bmpPath, ImageFormat.Bmp);

多螢幕支援

單一壁紙會延伸至所有螢幕。若需各螢幕不同壁紙,需呼叫 IDesktopWallpaper (Windows 8+ COM 介面) 或使用 SystemParametersInfo(SPI_SETDESKWALLPAPER) 搭配登錄機碼 HKCU\Control Panel\Desktop\Wallpaper 手動拼貼大圖。

高 DPI / 縮放考量

  • Windows 10/11 自動縮放壁紙至螢幕解析度。
  • 建議提供至少 1920x1080 或 3840x2160 解析度圖片,避免模糊。
  • 若應用程式啟用 DpiAwareness,Screen.Bounds 回傳實體像素,轉檔時可直接使用。

進階:命令列工具版本

// Program.cs - 適合排程/捷徑呼叫
class Program
{
    static int Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("用法: SetWallpaper.exe <圖片完整路徑>");
            return 1;
        }
        string path = args[0];
        if (!File.Exists(path)) { Console.Error.WriteLine("找不到檔案"); return 2; }

        string bmpPath = path;
        string ext = Path.GetExtension(path).ToLowerInvariant();
        if (ext != ".bmp")
        {
            try
            {
                using var img = Image.FromFile(path);
                bmpPath = Path.ChangeExtension(Path.GetTempFileName(), ".bmp");
                img.Save(bmpPath, ImageFormat.Bmp);
            }
            catch (Exception ex) { Console.Error.WriteLine($"轉檔失敗: {ex.Message}"); return 3; }
        }

        bool ok = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, bmpPath, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
        if (!ok) Console.Error.WriteLine($"API 失敗: {Marshal.GetLastWin32Error()}");

        // 清理暫存
        if (bmpPath != path && File.Exists(bmpPath)) File.Delete(bmpPath);
        return ok ? 0 : 4;
    }
    // ... P/Invoke 宣告同上
}

常見錯誤代碼對照

錯誤碼常數說明
87ERROR_INVALID_PARAMETER路徑非 BMP、檔案不存在、路徑過長 (MAX_PATH 260)
5ERROR_ACCESS_DENIED權限不足 (受限桌面、系統帳號)
2ERROR_FILE_NOT_FOUND檔案路徑錯誤
1460ERROR_TIMEOUT廣播 WM_SETTINGCHANGE 逾時 (其他視窗未回應)

替代方案比較

方法優點缺點
SystemParametersInfo (本文)無相依、支援 XP+、程式碼簡單僅 BMP、單一壁紙、無轉換/縮放選項
IDesktopWallpaper (COM)多螢幕獨立壁紙、支援 JPG/PNG、位置模式 (填滿/置中/平鋪)需 Windows 8+、COM Interop 較複雜
登錄機碼 + WM_SETTINGCHANGE可控制 WallpaperStyle/TileWallpaper非官方支援、需手動廣播、相容性風險
PowerShell / cmd無需編譯、腳本化啟動較慢、依賴外部程式
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved