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 Sep 2, 2020

Possible to programmatically set wallpapers for individual monitors? Just started searching so seeing where this session ends. Only tool I've seen that does this "well?" in the least gimmicky fashion is probably DisplayFusion.

@Drarig29
Copy link
Author

Drarig29 commented Sep 2, 2020

The straightforward way to do it is to join your wallpapers in a bigger image (as said here).

But it's an old technique I think. I know Windows 10 natively supports this now (see here) but I don't know how the Windows settings do it under the hood... maybe it makes a bigger wallpaper too?

It would be interesting to investigate in the registry for example. You could look at the values in HKEY_CURRENT_USER\Control Panel\Desktop, you can find information about the wallpaper filename in it... maybe check if it is set... I don't know 😉

And I found an open source tool which apparently can set a different wallpaper for each monitor and the last updates are from 2018... Maybe it uses a modern approach. I didn't look at the code.

https://sourceforge.net/projects/dualmonitortool/

@iKlsR
Copy link

iKlsR commented Sep 3, 2020

Hmm. The issue with it on Win 10 is that it's not persistent for some reason. I believe ultimately I could go with the stitching technique, thanks much for the links. Will do some more research/

@iKlsR
Copy link

iKlsR commented Sep 8, 2020

Just a followup, dual monitor tool is working perfectly with a triple monitor setup. No issues nearing a week now. Thanks again.

@Drarig29
Copy link
Author

Drarig29 commented Sep 9, 2020

You're welcome!

@Haapavuo
Copy link

Haapavuo commented Jan 15, 2021

For me it only sets black for all screens when trying to set a jpg file. I have one 4K monitor and one 1080p monitor connected. All just black. Does the same when using Set() or SilentSet().

EDIT: I found out that it changes Background to Solid color mode in Windows settings. Any idea why is that? It does not update any image there.

EDIT2: I am an idiot. I used a relative file path instead of an absolute path.

@Drarig29
Copy link
Author

Ah ah, it happens to the best of us 😅

@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