using Microsoft.Win32; using System; using System.Drawing; using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Linq; // A program that will periodically download an image from Unsplash and // change your Windows desktop background to the downloaded image // Compilation: csc /out:unsplash.exe /t:winexe .\Program.cs /nologo /o // After compilation, drop the executable in a folder and run it with some command line args // args[0] = minutes (how often to change the background image // args[1] = search terms to find your desire type of wallpaper // Example invocation: // unsplash.exe 60 nature,scenary // This will change the wallpaper every hour from category of nature and scenary namespace Unsplash { // Very simple logger implementation with a lock! public class FileLogger { protected readonly object locker = new object(); const string LOG_FILE_PATH = "application.log"; const bool ALSO_LOG_TO_STDOUT = true; public void Log(string message) { lock (locker) { using (var streamWriter = new StreamWriter(LOG_FILE_PATH, append: true)) { var line = string.Format("{0} \t {1}", DateTime.Now.ToString("MM/d/yyyy hh:mm:ss tt zzzz"), message); streamWriter.WriteLine(line); if (ALSO_LOG_TO_STDOUT) Console.WriteLine(line); } } } } class Program { const int ONE_HOUR_MS = 60 * 60 * 1000; const int FIFTEEN_MINUTES_MS = ONE_HOUR_MS / 4; const string IMAGE_DIR_NAME = "Unsplash"; const int DEFAULT_SEARCH_INTERVAL_MINUTES = 15; const string DEFAULT_SEARCH_CATEGORY = "nature,scenary,landmarks"; // Arg 0 = width, Arg 1 = height, Arg 2 = search terms (comma separated) const string SEARCH_URL_TEMPLATE = "https://source.unsplash.com/{0}x{1}/?{2}"; static string url; static FileLogger logger = new FileLogger(); static uint timerCallCount = 0; static void Main(string[] args) { SetAppBasePath(); logger.Log("Application started"); if (args.Length < 2) { logger.Log(String.Format("Setting default arguments since no arguments were specified. Interval = {0} minutes, search category = {1}", DEFAULT_SEARCH_INTERVAL_MINUTES, DEFAULT_SEARCH_CATEGORY)); args = new string[] { DEFAULT_SEARCH_INTERVAL_MINUTES.ToString(), DEFAULT_SEARCH_CATEGORY }; } var searchTerms = args[1]; var screenBounds = Screen.AllScreens.Where(x => x.Primary).First().Bounds; logger.Log("Primary screen bounds: " + screenBounds.ToString()); url = string.Format(SEARCH_URL_TEMPLATE, screenBounds.Width, screenBounds.Height, searchTerms); if (int.TryParse(args[0], out int interval)) { interval *= 60 * 1000; // convert minutes to milliseconds if (interval < FIFTEEN_MINUTES_MS) interval = FIFTEEN_MINUTES_MS; } else { interval = ONE_HOUR_MS; } new System.Threading.Timer(Tick, null, 0, interval); while (true) System.Threading.Thread.Sleep(1000); } private static void SetAppBasePath() { string imageDownloadDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, IMAGE_DIR_NAME); if (!Directory.Exists(imageDownloadDir)) { Directory.CreateDirectory(imageDownloadDir); } Directory.SetCurrentDirectory(imageDownloadDir); } private static void Tick(object state) { string imageFileName = ImageDownloader.Download(url); var logMessage = string.Format("Tick count: {0}. Setting wallpaper to {1}", ++timerCallCount, imageFileName); logger.Log(logMessage); Wallpaper.Set(imageFileName, Wallpaper.Style.Fill); } } class ImageDownloader { private static string Extension(string mimeType) { switch (mimeType) { case "image/jpeg": return ".jpg"; case "image/png": return ".png"; default: return ""; } } public static string Download(string url) { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var request = (HttpWebRequest)WebRequest.Create(url); var response = (HttpWebResponse)request.GetResponse(); var responseUri = response.ResponseUri; string imageFileName = new FileInfo(response.ResponseUri.LocalPath).Name + Extension(response.ContentType); using (Stream receiveStream = response.GetResponseStream()) using (Stream outputStream = File.OpenWrite(imageFileName)) { byte[] buffer = new byte[4096]; int bytesRead; do { bytesRead = receiveStream.Read(buffer, 0, buffer.Length); outputStream.Write(buffer, 0, bytesRead); } while (bytesRead != 0); } return imageFileName; } } public sealed class 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 { Fill, Fit, Span, Tile, Center, Stretch } public static void Set(string filename, Style style) { Image img = Image.FromFile(filename); 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.Fill) { key.SetValue(@"WallpaperStyle", "10"); key.SetValue(@"TileWallpaper", "0"); } if (style == Style.Fit) { key.SetValue(@"WallpaperStyle", "6"); key.SetValue(@"TileWallpaper", "0"); } if (style == Style.Span) // Windows 8 or newer only! { key.SetValue(@"WallpaperStyle", "22"); key.SetValue(@"TileWallpaper", "0"); } if (style == Style.Stretch) { key.SetValue(@"WallpaperStyle", "2"); key.SetValue(@"TileWallpaper", "0"); } if (style == Style.Tile) { key.SetValue(@"WallpaperStyle", "0"); key.SetValue(@"TileWallpaper", "1"); } if (style == Style.Center) { key.SetValue(@"WallpaperStyle", "0"); key.SetValue(@"TileWallpaper", "0"); } SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, tempPath, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE); } } }