Skip to content

Instantly share code, notes, and snippets.

@Drarig29
Last active March 4, 2024 12:29
Show Gist options
  • Save Drarig29/4aa001074826f7da69b5bb73a83ccd39 to your computer and use it in GitHub Desktop.
Save Drarig29/4aa001074826f7da69b5bb73a83ccd39 to your computer and use it in GitHub Desktop.
Temporarily set a desktop wallpaper without polluting the Windows settings history
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
public enum WallpaperStyle
{
Fill,
Fit,
Stretch,
Tile,
Center,
Span,
}
public static class Wallpaper
{
private const string DESKTOP_REG_PATH = @"Control Panel\Desktop";
private const string HISTORY_REG_PATH = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Wallpapers";
private const string WALLPAPER_STYLE_REG_PATH = "WallpaperStyle";
private const string TILE_WALLPAPER_REG_PATH = "TileWallpaper";
private const int HISTORY_MAX_ENTRIES = 5;
const int SPI_SETDESKWALLPAPER = 20;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDWININICHANGE = 0x02;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
private static State? _backupState;
private static bool _historyRestored;
private struct Config
{
public int Style;
public bool IsTile;
}
private struct State
{
public Config Config;
public string[] History;
public string Wallpaper;
}
private static int GetRegistryValue(RegistryKey key, string name, int defaultValue)
{
return int.Parse((string)key.GetValue(name) ?? defaultValue.ToString());
}
private static bool GetRegistryValue(RegistryKey key, string name, bool defaultValue)
{
return ((string)key.GetValue(name) ?? (defaultValue ? "1" : "0")) == "1";
}
private static void SetRegistryValue(RegistryKey key, string name, int value)
{
key.SetValue(name, value.ToString());
}
private static void SetRegistryValue(RegistryKey key, string name, bool value)
{
key.SetValue(name, value ? "1" : "0");
}
private static Config GetWallpaperConfig()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(DESKTOP_REG_PATH, true);
return new Config
{
Style = GetRegistryValue(key, WALLPAPER_STYLE_REG_PATH, 0),
IsTile = GetRegistryValue(key, TILE_WALLPAPER_REG_PATH, false),
};
}
private static void SetWallpaperConfig(Config value)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(DESKTOP_REG_PATH, true);
SetRegistryValue(key, WALLPAPER_STYLE_REG_PATH, value.Style);
SetRegistryValue(key, TILE_WALLPAPER_REG_PATH, value.IsTile);
}
private static void SetStyle(WallpaperStyle style)
{
switch (style)
{
case WallpaperStyle.Fill:
SetWallpaperConfig(new Config { Style = 10, IsTile = false });
break;
case WallpaperStyle.Fit:
SetWallpaperConfig(new Config { Style = 6, IsTile = false });
break;
case WallpaperStyle.Stretch:
SetWallpaperConfig(new Config { Style = 2, IsTile = false });
break;
case WallpaperStyle.Tile:
SetWallpaperConfig(new Config { Style = 0, IsTile = true });
break;
case WallpaperStyle.Center:
SetWallpaperConfig(new Config { Style = 0, IsTile = false });
break;
case WallpaperStyle.Span: // Windows 8 or newer only
SetWallpaperConfig(new Config { Style = 22, IsTile = false });
break;
default:
throw new ArgumentOutOfRangeException(nameof(style));
}
}
private static void ChangeWallpaper(string filename)
{
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, filename, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
}
private static void RestoreHistory()
{
if (_historyRestored) return;
if (!_backupState.HasValue)
throw new Exception("You must call BackupState() before.");
var backupState = _backupState.Value;
using (var key = Registry.CurrentUser.OpenSubKey(HISTORY_REG_PATH, true))
{
for (var i = 0; i < HISTORY_MAX_ENTRIES; i++)
if (backupState.History[i] != null)
key.SetValue($"BackgroundHistoryPath{i}", backupState.History[i], RegistryValueKind.String);
}
_historyRestored = true;
}
/// <summary>
/// Backups the current wallpaper state (style and history).
/// </summary>
public static void BackupState()
{
var history = new string[HISTORY_MAX_ENTRIES];
using (var key = Registry.CurrentUser.OpenSubKey(HISTORY_REG_PATH, true)) {
for (var i = 0; i < history.Length; i++)
history[i] = (string)key.GetValue($"BackgroundHistoryPath{i}");
}
_backupState = new State
{
Config = GetWallpaperConfig(),
History = history,
Wallpaper = history[0],
};
_historyRestored = false;
}
/// <summary>
/// Restores the state (style, wallpaper and history) before any Set() method.
/// </summary>
public static void RestoreState()
{
if (!_backupState.HasValue)
throw new Exception("You must call BackupState() before.");
SetWallpaperConfig(_backupState.Value.Config);
ChangeWallpaper(_backupState.Value.Wallpaper);
RestoreHistory();
_backupState = null;
}
/// <summary>
/// Sets the wallpaper without changing its style.
/// </summary>
public static void Set(string filename)
{
BackupState();
ChangeWallpaper(filename);
}
/// <summary>
/// Sets the wallpaper with the given style.
/// </summary>
public static void Set(string filename, WallpaperStyle style)
{
BackupState();
SetStyle(style);
ChangeWallpaper(filename);
}
/// <summary>
/// Sets the wallpaper without changing its style nor the history in Windows settings.
/// </summary>
public static void SilentSet(string filename)
{
Set(filename);
RestoreHistory();
}
/// <summary>
/// Sets the wallpaper with the given style without changing the history in Windows settings.
/// </summary>
public static void SilentSet(string filename, WallpaperStyle style)
{
Set(filename, style);
RestoreHistory();
}
}
@iKlsR
Copy link

iKlsR commented Jan 15, 2021

I was searching for this thread for a while now to update. I ended up finding https://rocksdanister.github.io/lively/ since my initial goal was just to set wallpapers across 3 monitors and it's a lot more intuitive and easier to use.

@Drarig29
Copy link
Author

Nice!

@henior2
Copy link

henior2 commented Jan 25, 2022

I added solid color backup support here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment