使用 .NET 中的代码更改桌面墙纸
Posted
技术标签:
【中文标题】使用 .NET 中的代码更改桌面墙纸【英文标题】:Change desktop wallpaper using code in .NET 【发布时间】:2010-11-06 21:29:26 【问题描述】:如何使用 C# 代码更改桌面墙纸?
【问题讨论】:
【参考方案1】:这是从我在一两年前编写的应用程序中提取的一个类:
public sealed class Wallpaper
Wallpaper()
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);
public enum Style : int
Tiled,
Centered,
Stretched
public static void Set(Uri uri, Style style)
System.IO.Stream s = new System.Net.WebClient().OpenRead(uri.ToString());
System.Drawing.Image img = System.Drawing.Image.FromStream(s);
string tempPath = Path.Combine(Path.GetTempPath(), "wallpaper.bmp");
img.Save(tempPath, System.Drawing.Imaging.ImageFormat.Bmp);
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
if (style == Style.Stretched)
key.SetValue(@"WallpaperStyle", 2.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Centered)
key.SetValue(@"WallpaperStyle", 1.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Tiled)
key.SetValue(@"WallpaperStyle", 1.ToString());
key.SetValue(@"TileWallpaper", 1.ToString());
SystemParametersInfo(SPI_SETDESKWALLPAPER,
0,
tempPath,
SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
我没有对它进行广泛的测试,所以使用风险自负。
【讨论】:
不确定windows是否支持jpeg作为壁纸......它在设置为壁纸之前将其他图像转换为bmp(如果我错了,请纠正我)如果是这种情况你必须在你的代码 我一直将它用于 jpeg 就好了。仅仅因为它在上面的代码中称为 bmp 并不限制它。它可能适用于 png 和 gif 以及其他格式,但我尚未验证。 @NeilN 这很好,但是如果想添加一个复选框“空白壁纸”以将壁纸设置为黑色怎么办! 仅供参考:此代码不适用于 PNG。我必须先手动转换为 BMP。 哇!它完全有效,它支持 JPG ......真的很好。恭喜。【参考方案2】:基于this useful answer,我也做了my own app来设置壁纸匹配屏幕分辨率。
但是注册表设置错误。以下是正确的值(在 Win 7、Win 8.1、Win 10 上测试)。
if (style == Style.Fill)
key.SetValue(@"WallpaperStyle", 10.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Fit)
key.SetValue(@"WallpaperStyle", 6.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Span) // Windows 8 or newer only!
key.SetValue(@"WallpaperStyle", 22.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Stretch)
key.SetValue(@"WallpaperStyle", 2.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Tile)
key.SetValue(@"WallpaperStyle", 0.ToString());
key.SetValue(@"TileWallpaper", 1.ToString());
if (style == Style.Center)
key.SetValue(@"WallpaperStyle", 0.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
【讨论】:
为什么使用 (number).ToString()?为什么不是“(数字)”? 当然可以。这是一个概念问题——我这样写是因为别人这样做了。【参考方案3】:如果您想临时设置桌面墙纸而不污染 Windows 设置历史记录,这是我提出的要点。
它会在设置临时壁纸之前备份wallpaper history(存储在注册表中),之后您可以将其恢复。
https://gist.github.com/Drarig29/4aa001074826f7da69b5bb73a83ccd39
【讨论】:
【参考方案4】:如果你在 c# 中使用 UWP,这是我找到的代码 here
using Windows.System.UserProfile;
// Pass in a relative path to a file inside the local appdata folder
async Task<bool> SetWallpaperAsync(string localAppDataFileName)
bool success = false;
if (UserProfilePersonalizationSettings.IsSupported())
var uri = new Uri("ms-appx:///Local/" + localAppDataFileName);
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
UserProfilePersonalizationSettings profileSettings = UserProfilePersonalizationSettings.Current;
success = await profileSettings.TrySetWallpaperImageAsync(file);
其实我觉得可以更简单 file.png 是您的照片,可以是 .jpg 或其他格式
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("file.png", CreationCollisionOption.ReplaceExisting);
bool success = false;
UserProfilePersonalizationSettings profileSettings = UserProfilePersonalizationSettings.Current;
success = await profileSettings.TrySetWallpaperImageAsync(file);
【讨论】:
【参考方案5】:调整 Neal N 对 Gif 的回答:
private const int SPI_SETDESKWALLPAPER = 20;
private const int SPIF_UPDATEINIFILE = 0x01;
private const int SPIF_SENDWININICHANGE = 0x02;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
public enum Style : int
Tiled,
Centered,
Stretched
/// <summary>
/// Loops numFrames times, animating the desktop background as the given gif.
/// Remember this will sorta bog down your computer, and probably isn't best to be running 24/7.
/// If numFrames is negative this will loop forever
/// </summary>
/// <param name="gifPath">The gif to be animated</param>
/// <param name="transparencyReplace">If the gif has transparency, it will be "replaced" with this color.</param>
/// <param name="framesPerSecond">How many frames to play per second. This is a max: most likely it will be a little slower than this especially at first.</param>
/// <param name="style">Whether to tile, center, or stretch each gif frame as it's played.</param>
/// <param name="numFrames">The number of frames to play. If negative, this method will loop forever.</param>
public static void SetDesktopBackgroundAsGifAsync(string gifPath, System.Drawing.Color transparencyReplace, int framesPerSecond, Style style, int numFrames)
Thread workerThread = new Thread(() => SetDesktopBackgroundAsGif(gifPath, transparencyReplace, framesPerSecond, style, numFrames));
workerThread.Start();
/// <summary>
/// Loops numFrames times, animating the desktop background as the given gif.
/// Remember this will sorta bog down your computer, and probably isn't best to be running 24/7.
/// If num frames is negative this will loop forever.
//// <summary>
/// <param name="gifPath">The gif to be animated</param>
/// <param name="backgroundImage">Image to render the gif on top of (because of transparency)</param>
/// <param name="framesPerSecond">How many frames to play per second. This is a max: most likely it will be a little slower than this.</param>
/// <param name="style">Whether to tile, center, or stretch each gif frame as it's played.</param>
/// <param name="numFrames">The number of frames to play. If negative, this method will loop forever.</param>
public static void SetDesktopBackgroundAsGifAsync(string gifPath, System.Drawing.Image backgroundImage, int framesPerSecond, Style style, int numFrames)
Thread workerThread = new Thread(() => SetDesktopBackgroundAsGif(gifPath, backgroundImage, framesPerSecond, style, numFrames));
workerThread.Start();
/// <summary>
/// Loops numFrames times, animating the desktop background as the given gif.
/// Remember this will sorta bog down your computer, and probably isn't best to be running 24/7.
/// if numFrames is negative this will loop forever
/// </summary>
/// <param name="gifPath">The gif to be animated</param>
/// <param name="transparencyReplace">If the gif has transparency, it will be "replaced" with this color.</param>
/// <param name="framesPerSecond">How many frames to play per second. This is a max: most likely it will be a little slower than this.</param>
/// <param name="style">Whether to tile, center, or stretch each gif frame as it's played.</param>
/// <param name="numFrames">The number of frames to play. If negative, this method will loop forever.</param>
public static void SetDesktopBackgroundAsGif(string gifPath, System.Drawing.Color transparencyReplace, int framesPerSecond, Style style, int numFrames)
if (!File.Exists(gifPath))
throw new Exception("Given gif: '" + gifPath + "' not found");
FileStream gifFile = new FileStream(gifPath, FileMode.Open);
GifBitmapDecoder gifDecoder = new GifBitmapDecoder(gifFile, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
if (gifDecoder.Frames.Count == 0)
throw new Exception("No frames in given gif");
Bitmap backgroundImage = new Bitmap(gifDecoder.Frames[0].PixelWidth, gifDecoder.Frames[0].PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using(Graphics g = Graphics.FromImage(backgroundImage))
g.FillRectangle(new System.Drawing.SolidBrush(transparencyReplace), 0, 0, gifDecoder.Frames[0].PixelWidth, gifDecoder.Frames[0].PixelHeight);
gifFile.Close();
SetDesktopBackgroundAsGif(gifPath, backgroundImage, framesPerSecond, style, numFrames);
/// <summary>
/// Loops infinitely, animating the desktop background as the given gif.
/// Remember this will sorta bog down your computer, and probably isn't best to be running 24/7.
/// </summary>
/// <param name="gifPath">The gif to be animated</param>
/// <param name="backgroundImage">Image to render the gif on top of (because of transparency)</param>
/// <param name="framesPerSecond">How many frames to play per second. This is a max: most likely it will be a little slower than this.</param>
/// <param name="style">Whether to tile, center, or stretch each gif frame as it's played.</param>
/// <param name="numFrames">The number of frames to play. If negative, this method will loop forever.</param>
private static void SetDesktopBackgroundAsGif(string gifPath, System.Drawing.Image backgroundImage, int framesPerSecond, Style style, int numFrames)
if (!File.Exists(gifPath))
throw new Exception("Given gif: '" + gifPath + "' not found");
FileStream gifFile = new FileStream(gifPath, FileMode.Open);
GifBitmapDecoder gifDecoder = new GifBitmapDecoder(gifFile, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
if (gifDecoder.Frames.Count == 0)
throw new Exception("No frames in given gif");
Console.WriteLine("Saving frames to temporary files:");
int numFramesSoFar = 0;
for (int i = 0; i < gifDecoder.Frames.Count; i++)
BitmapFrame gifFrame = gifDecoder.Frames[i];
PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(gifFrame);
MemoryStream pngStream = new MemoryStream();
pngEncoder.Save(pngStream);
Image frameImage = Image.FromStream(pngStream);
Bitmap bmp = new Bitmap(frameImage.Width, frameImage.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
g.DrawImage(backgroundImage, 0, 0);
g.DrawImageUnscaled(frameImage, 0, 0);
string tempPath = Path.Combine(Path.GetTempPath(), gifPath + i + ".bmp");
bmp.Save(tempPath, System.Drawing.Imaging.ImageFormat.Bmp);
Console.WriteLine("Saved frame " + i);
numFramesSoFar++;
if (numFrames >= 0 && numFramesSoFar >= numFrames) break;
Console.WriteLine("Setting frames to desktop background at about " + framesPerSecond + " FPS");
// 1.0/... to convert to seconds per frame (instead of frames per second)
// * 1000 to convert to milliseconds per frame
// * 1000 to convert to microseconds per frame
// * 10 to convert to 0.1s of microseconds per frame = 100s of nanoseconds per frame
long ticksBetweenFrames = (long)Math.Round(1.0 / framesPerSecond) * 1000*1000*10;
Stopwatch timer = new Stopwatch();
timer.Start();
numFramesSoFar = 0;
while(numFrames < 0 || numFramesSoFar < numFrames)
for (int i = 0; i < gifDecoder.Frames.Count; i++)
// Sleep until we're at the desired frame rate, if needed.
if(ticksBetweenFrames > timer.ElapsedTicks)
Thread.Sleep(new TimeSpan(Math.Max(0, ticksBetweenFrames - timer.ElapsedTicks)));
timer.Restart();
// From http://***.com/a/1061682/2924421
string filePath = Path.Combine(Path.GetTempPath(), "wallpaper" + i + ".bmp");
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
if (style == Style.Stretched)
key.SetValue(@"WallpaperStyle", 2.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Centered)
key.SetValue(@"WallpaperStyle", 1.ToString());
key.SetValue(@"TileWallpaper", 0.ToString());
if (style == Style.Tiled)
key.SetValue(@"WallpaperStyle", 1.ToString());
key.SetValue(@"TileWallpaper", 1.ToString());
SystemParametersInfo(SPI_SETDESKWALLPAPER,
0,
filePath,
SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
numFramesSoFar++;
if (numFrames >= 0 && numFramesSoFar >= numFrames) break;
gifFile.Close();
还要注意需要用到:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
最后,右键单击您的项目,添加引用,然后(在程序集和框架中)添加 Presentation Core、System.Xaml 和 WindowsBase。
然后右键单击您的项目并进入属性,并确保目标框架是 .Net Framework 4.5。如果您更改此设置,您可能需要重新启动 Visual Studio。
【讨论】:
当然它会使机器陷入困境。有一个阻塞的无限循环,随意调用Thread.Sleep
。有更好的方法来等待时间过去。
不,我认为这与对 SystemParametersInfo 的调用有关(例如,每秒 10 次)。桌面不应该像那样刷新。这可以通过“explorer”从 0% 到 6-12% 的 cpu 使用率来证明,只需运行一个调用此方法的程序即可。不过,我调整了我的答案,添加了异步方法并使对 Thread.Sleep 的调用减少了垃圾邮件(仅在需要时发生)。还有其他明显的问题吗?
我考虑过调整它以通过另一种方法停止线程,但这似乎对于一种可能不是那么好的做法的方法来说太过分了。
再想一想,我添加了一个 numFrames 参数,该参数指定在此方法停止之前要设置动画的帧数。如果是否定的,它将永远存在。
等着听什么?以上是关于使用 .NET 中的代码更改桌面墙纸的主要内容,如果未能解决你的问题,请参考以下文章
有没有人使用过嵌入在 .NET 桌面应用程序中的 Open Office?