From 122976ecf53f1736831f54c014bb2d83e293ced3 Mon Sep 17 00:00:00 2001 From: ORelio Date: Sun, 11 Feb 2018 21:15:35 +0100 Subject: [PATCH] Add support for Windows 7 and 8 lockscreen Also allow easy restore of default configuration with new lockscreen --restore command line switch --- README-En.txt | 6 +- README-Fr.txt | 6 +- README.md | 14 +- Scripts/restore-lockscreen.bat | 6 +- SpotlightDownloader/ImageEncoder.cs | 132 +++++++++++++ SpotlightDownloader/Lockscreen.cs | 183 +++++++++++++++++- SpotlightDownloader/Program.cs | 54 ++++-- .../SpotlightDownloader.csproj | 2 + SpotlightDownloader/WindowsVersion.cs | 124 ++++++++++++ 9 files changed, 491 insertions(+), 36 deletions(-) create mode 100644 SpotlightDownloader/ImageEncoder.cs create mode 100644 SpotlightDownloader/WindowsVersion.cs diff --git a/README-En.txt b/README-En.txt index 40d2837..96eebd4 100644 --- a/README-En.txt +++ b/README-En.txt @@ -1,5 +1,5 @@ ===================================================== -==== SpotlightDL v1.1 - By ORelio - Microzoom.fr ==== +==== SpotlightDL v1.2 - By ORelio - Microzoom.fr ==== ===================================================== Thanks for dowloading SpotlightDL! @@ -10,7 +10,7 @@ SpotlightDL can also define images as wallpaper and system-wide lockscreen image It is useful in the following use cases: - Download the whole Spotlight library with maximum image resolution and metadata - Define Spotlight images as wallpaper, not only on Windows 10 but also on previous versions - - Define Spotlight images as global lock screen: without ads, and without any user being logged in + - Define Spotlight images as global lock screen on Windows 7, 8 and 10, without ads on Windows 10 - Chain SpotlightDL with your own scripts and apps by taking advantage of the url mode ============ @@ -38,7 +38,7 @@ update-lockscreen Windows folder and clear the lockscreen cache to force a lockscreen refresh. restore-lockscreen - This script restores the default lock screen image from Windows 10 + This script restores the default lock screen image. This script must be run as administrator. generate-manual diff --git a/README-Fr.txt b/README-Fr.txt index a038601..a8beada 100644 --- a/README-Fr.txt +++ b/README-Fr.txt @@ -1,5 +1,5 @@ ====================================================== -==== SpotlightDL v1.1 - Par ORelio - Microzoom.fr ==== +==== SpotlightDL v1.2 - Par ORelio - Microzoom.fr ==== ====================================================== Merci d'avoir téléchargé SpotlightDL! @@ -10,7 +10,7 @@ SpotlightDL peut également définir des images en tant que fond d'écran ou sur Ce programme est utile dans les cas suivants : - Télécharger toute la bibliothèque d'images en définition maximale, avec fichiers de métadonnées - Définir les images en tant que fond d'écran, non seulement sous Windows 10 mais aussi sur les versions précédentes - - Définir les images sur l'écran de verouillage global, sans les publicités, et même lorsqu'aucune session n'est ouverte + - Définir les images sur l'écran de verouillage global sous Windows 7, 8 et 10, sans les publicités sous Windows 10 - Utiliser SpotlightDL dans vos propres scripts et programmes en appelant la fonction récupérant les URL des images ============= @@ -37,7 +37,7 @@ update-lockscreen et vide le cache de l'écran de verouillage afin d'en forcer la mise à jour immédiate. restore-lockscreen - Ce script restaure l'écran de verouillage par défaut de Windows 10 + Ce script restaure l'écran de verouillage par défaut. Il requiert les droits administrateur. generate-manual diff --git a/README.md b/README.md index de0e297..a54765c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ SpotlightDL can also define images as wallpaper and system-wide lockscreen image It is useful in the following use cases: - Download the whole Spotlight library with maximum image resolution and metadata - Define Spotlight images as wallpaper, not only on Windows 10 but also on previous versions - - Define Spotlight images as global lock screen: without ads, and without any user being logged in + - Define Spotlight images as global lock screen on Win7/8/10, removing the ads on Windows 10 - Chain SpotlightDL with your own scripts and apps by taking advantage of the url mode # How to use @@ -52,7 +52,7 @@ Spotlight API URL was originally found in this [file](https://github.com/KoalaBR ## Global lock screen -The global lock screen image is stored as `C:\Windows\Web\Screen\img100.jpg`. +The global lock screen image for Windows 8 and 10 is stored as `C:\Windows\Web\Screen\img100.jpg`. SpotlightDL backups the image as `img200.jpg` if it does not already exists, then overwrite this file. The lock screen image cache, located at `C:\ProgramData\Microsoft\Windows\SystemData\S-1-5-18\ReadOnly\LockScreen_Z`, must be cleared for the change to take effect. @@ -63,6 +63,16 @@ Then, programs running as administrator can overwrite the lockscreen image and c This way of replacing the lockscreen is basically a C# implementation of [this script](https://www.reddit.com/r/PowerShell/comments/5fglby/powershell_to_set_windows_10_lockscreen/daoepvj/), avoiding the use of the `takeown` and `iacls` commands which are not reliable due to a [localization issue](http://community.idera.com/powershell/ask_the_experts/f/powershell_for_windows-12/10227/trying-to-make-a-takeown-exe-cmdlet-but-locales-is-causing-a-problem). +Windows 7 support is also implemented through the [OEMBackground](https://www.askvg.com/windows-7-supports-login-screen-customization-without-3rd-party-software-how-to-instructions-inside/) feature: + +```` +[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Background] +"OEMBackground"=dword:00000001 +```` + +Then the image has to be placed in `C:\Windows\System32\oobe\info\backgrounds\backgroundDefault.jpg` +Windows 7 enforces a limit of 250 KiB so SpotlightDL will recompress the image to the highest quality fitting in that limit. + # License SpotlightDL is provided under [CDDL-1.0](http://opensource.org/licenses/CDDL-1.0) ([Why?](http://qstuff.blogspot.fr/2007/04/why-cddl.html)). diff --git a/Scripts/restore-lockscreen.bat b/Scripts/restore-lockscreen.bat index 7c75748..f74c871 100644 --- a/Scripts/restore-lockscreen.bat +++ b/Scripts/restore-lockscreen.bat @@ -1,9 +1,9 @@ @echo off cd "%~dp0" -:: This script restores the default lock screen image from Windows 10. +:: This script restores the default lock screen image. :: SpotlightDownloader performs a backup before overwriting the file, -:: so we just need to define the backup file as lockscreen image. +:: so we just need to ask it to restore the backup. net session > nul 2>&1 if not "%errorlevel%" == "0" ( @@ -12,4 +12,4 @@ if not "%errorlevel%" == "0" ( exit ) -SpotlightDownloader lockscreen --from-file "%systemroot%\Web\Screen\img200.jpg" \ No newline at end of file +SpotlightDownloader lockscreen --restore \ No newline at end of file diff --git a/SpotlightDownloader/ImageEncoder.cs b/SpotlightDownloader/ImageEncoder.cs new file mode 100644 index 0000000..7a139f3 --- /dev/null +++ b/SpotlightDownloader/ImageEncoder.cs @@ -0,0 +1,132 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; + +namespace SharpTools +{ + /// + /// Wrapper class for generating images + /// By ORelio (c) 2018 - CDDL 1.0 + /// + /// Jpeg part of this class is written by Pratik and davidwroxy from StackOverflow + /// + /// + public static class ImageEncoder + { + /// + /// Encode an input image to a Jpeg file below the requested size, trying to achieve maximum possible quality. + /// + /// Input image path + /// Output jpeg image path + /// Maximum size in bytes. If omitted, the Jpeg file is created with maximum available quality + /// TRUE if thefile was successfully generated + public static bool CreateJpeg(string inputPath, string outputPath, long maximumSizeBytes = Int64.MaxValue) + { + return CreateJpeg(Image.FromFile(inputPath), outputPath, maximumSizeBytes); + } + + /// + /// Encode an input image to a Jpeg file below the requested size + /// + /// Input image + /// Output jpeg image path + /// Maximum size in bytes. If omitted, the Jpeg file is created with maximum available quality + /// TRUE if thefile was successfully generated + public static bool CreateJpeg(Image image, string outputPath, long maximumSizeBytes = Int64.MaxValue) + { + long outputFileSize = Int64.MaxValue; + int quality = 100; + do + { + SaveJpeg(outputPath, image, quality); + outputFileSize = new FileInfo(outputPath).Length; + quality--; + } + while (quality >= 0 && outputFileSize > maximumSizeBytes); + return quality >= 0; + } + + /// + /// Encode an input image to a PNG file + /// + /// Input image path + /// Output png image path + public static void CreatePng(string inputPath, string outputPath) + { + CreatePng(Image.FromFile(inputPath), outputPath); + } + + /// + /// Encode an input image to a PNG file + /// + /// Input image + /// Output png image path + public static void CreatePng(Image image, string outputPath) + { + image.Save(outputPath, ImageFormat.Png); + } + + /// + /// Copy an input image file to an output path, converting image if necessary. Existing output file is overwritten. + /// + /// Input image path + /// Output image path + /// Thrown if a non-supported output format is requested and image needs converting. + public static void AutoCopyImageFile(string inputPath, string outputPath) + { + string intputExt = Path.GetExtension(inputPath).ToLower(); + string outputExt = Path.GetExtension(outputPath).ToLower(); + if (intputExt == outputExt) + { + File.Copy(inputPath, outputPath, true); + } + else if (outputExt == ".jpg") + { + CreateJpeg(inputPath, outputPath); + } + else if (outputExt == ".png") + { + CreatePng(inputPath, outputPath); + } + else throw new NotSupportedException(String.Format("Output file format '{0}' is not supported.", outputExt)); + } + + /// + /// Saves an image as a jpeg image, with the given quality + /// + /// Path to which the image would be saved. + /// An integer from 0 to 100, with 100 being the highest quality. + /// https://stackoverflow.com/a/4161930 + private static void SaveJpeg(string path, Image img, int quality) + { + if (quality < 0 || quality > 100) + throw new ArgumentOutOfRangeException("quality must be between 0 and 100."); + + // Encoder parameter for image quality + EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality); + // JPEG image codec + ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg"); + EncoderParameters encoderParams = new EncoderParameters(1); + encoderParams.Param[0] = qualityParam; + img.Save(path, jpegCodec, encoderParams); + } + + /// + /// Returns the image codec with the given mime type + /// + /// https://stackoverflow.com/a/4161930 + private static ImageCodecInfo GetEncoderInfo(string mimeType) + { + // Get image codecs for all image formats + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); + + // Find the correct image codec + for (int i = 0; i < codecs.Length; i++) + if (codecs[i].MimeType == mimeType) + return codecs[i]; + + return null; + } + } +} diff --git a/SpotlightDownloader/Lockscreen.cs b/SpotlightDownloader/Lockscreen.cs index 9f2007c..2f4c353 100644 --- a/SpotlightDownloader/Lockscreen.cs +++ b/SpotlightDownloader/Lockscreen.cs @@ -1,37 +1,200 @@ using System; +using System.Linq; using System.IO; using SharpTools; +using Microsoft.Win32; +using System.Drawing.Imaging; +using System.Drawing; namespace SharpTools { /// - /// Wrapper around the Windows 10 lockscreen + /// Wrapper around the Windows lockscreen image /// By ORelio (c) 2018 - CDDL 1.0 /// class Lockscreen { /// - /// Replace the Windows 10 global lockscreen + /// Replace the system lockscreen image for Windows 7, 8 and 10. /// /// Administrator permissions are required /// Path to the new .jpg file for global lockscreen public static void SetGlobalLockscreen(string path) { - string systemDataDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\Windows\SystemData"; - string lockscreenDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Web\Screen"; - string lockscreenPic = lockscreenDir + @"\img100.jpg"; - string lockscreenBak = lockscreenDir + @"\img200.jpg"; + if (WindowsVersion.IsMono) + { + throw new NotImplementedException("Lockscreen handling is not implemented for Mac and Linux using the Mono framework."); + } + else if (WindowsVersion.WinMajorVersion == 6 && WindowsVersion.WinMinorVersion == 1 /* Windows 7 */) + { + string lockscreenDir = Win7_InitDir(); + string lockscreenPic = String.Concat(lockscreenDir, Path.DirectorySeparatorChar, "backgroundDefault.jpg"); + PerformBackupReplace(lockscreenDir, true); + ImageEncoder.CreateJpeg(path, lockscreenPic, 256000); + Win7_RegistryKey(true); + + //Create a dummy backup if the machine contains no OEM background image + if (!File.Exists(lockscreenPic + ".bak")) + File.Create(lockscreenPic + ".bak").Close(); + } + else if ((WindowsVersion.WinMajorVersion == 6 && WindowsVersion.WinMajorVersion >= 2) /* Windows 8 and 10 */ + || WindowsVersion.WinMajorVersion >= 10 /* Windows 10 and future Windows versions - might not work on newer versions */) + { + string lockscreenDir = Win10_InitDir(); + PerformBackupReplace(lockscreenDir, false, path); + Win10_ClearCache(); + } + else + { + throw new NotImplementedException( + String.Format( + "Lockscreen handling is not implemented for Windows NT {0}.{1}", + WindowsVersion.WinMajorVersion, + WindowsVersion.WinMinorVersion + ) + ); + } + } + + /// + /// Restore the system default lockscreen image for Windows 7, 8 and 10. + /// + /// Administrator permissions are required + public static void RestoreDefaultGlobalLockscreen() + { + if (WindowsVersion.IsMono) + { + throw new NotImplementedException("Lockscreen handling is not implemented for Mac and Linux using the Mono framework."); + } + else if (WindowsVersion.WinMajorVersion == 6 && WindowsVersion.WinMinorVersion == 1 /* Windows 7 */) + { + string lockscreenDir = Win7_InitDir(); + string lockscreenPic = String.Concat(lockscreenDir, Path.DirectorySeparatorChar, "backgroundDefault.jpg"); + File.Delete(lockscreenPic); + RestoreBackup(lockscreenDir); + + //Dummy backup indicates there was no original OEM image on this machine, we need to disable OEMBackground + if (File.Exists(lockscreenPic) && new FileInfo(lockscreenPic).Length == 0) + { + File.Delete(lockscreenPic); + Win7_RegistryKey(false); + } + } + else if ((WindowsVersion.WinMajorVersion == 6 && WindowsVersion.WinMajorVersion >= 2) /* Windows 8 and 10 */ + || WindowsVersion.WinMajorVersion >= 10 /* Windows 10 and future Windows versions - might not work on newer versions */) + { + string lockscreenDir = Win10_InitDir(); + RestoreBackup(lockscreenDir); + Win10_ClearCache(); + } + else + { + throw new NotImplementedException( + String.Format( + "Lockscreen handling is not implemented for Windows NT {0}.{1}", + WindowsVersion.WinMajorVersion, + WindowsVersion.WinMinorVersion + ) + ); + } + } + + /// + /// Initialize permissions for the Windows 7 lock screen directory and return its path + /// + /// Windows 7 lock screen directory path + private static string Win7_InitDir() + { + string systemDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows) + (Environment.Is64BitOperatingSystem ? @"\Sysnative\oobe\" : @"\System32\oobe\"); + string lockscreenDir = systemDir + @"info\backgrounds"; + + if (!Directory.Exists(lockscreenDir)) + { + FileSystemAdmin.GrantAll(systemDir, false); + Directory.CreateDirectory(lockscreenDir); + } - FileSystemAdmin.GrantAll(systemDataDir, true); FileSystemAdmin.GrantAll(lockscreenDir, true); + return lockscreenDir; + } + + /// + /// Set the OEMBackground registry key for Windows 7 + /// + /// TRUE to enable OEMBackground + private static void Win7_RegistryKey(bool enable) + { + RegistryKey localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Default); + RegistryKey regLogonSettings = localMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Background"); + regLogonSettings.SetValue("OEMBackground", enable ? 1 : 0); + } - if (!File.Exists(lockscreenBak)) - File.Copy(lockscreenPic, lockscreenBak); - File.Copy(path, lockscreenPic, true); + /// + /// Initialize permissions for the Windows 8/10 lock screen directory and return its path + /// + /// Windows 8/10 lock screen directory path + private static string Win10_InitDir() + { + string lockscreenDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Web\Screen"; + FileSystemAdmin.GrantAll(lockscreenDir, true); + return lockscreenDir; + } + /// + /// Clear the Windows 8/10 lock screen cache to force lock screen refresh + /// + private static void Win10_ClearCache() + { + string systemDataDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\Windows\SystemData"; + FileSystemAdmin.GrantAll(systemDataDir, true); foreach (string cacheDir in Directory.EnumerateDirectories(systemDataDir, "LockScreen_*", SearchOption.AllDirectories)) foreach (string cacheFile in Directory.EnumerateFiles(cacheDir, "*.jpg", SearchOption.AllDirectories)) File.Delete(cacheFile); } + + /// + /// Perform backups of jpg and png files in the specified directory. Files with an existing backup are ignored. + /// + /// Target directory + /// If enabled, remove the original files after performing a backup + /// If specified, also replace all the original images with the specified image file + public static void PerformBackupReplace(string targetDir, bool removeOriginal, string overwriteWithImage = null) + { + foreach (string picture in Directory.GetFiles(targetDir, "*.jpg").Union(Directory.GetFiles(targetDir, "*.png"))) + { + string pictureBackup = picture + ".bak"; + + if (!File.Exists(pictureBackup)) + { + if (removeOriginal) + { + File.Move(picture, pictureBackup); + } + else File.Copy(picture, pictureBackup); + } + + if (overwriteWithImage != null) + { + if (!File.Exists(overwriteWithImage)) + throw new ArgumentException("The specified image file does not exist.", "overwriteWithImage"); + ImageEncoder.AutoCopyImageFile(overwriteWithImage, picture); + } + } + } + + /// + /// Restore backups of jpg and png files in the specified directory + /// + /// Target directory + private static void RestoreBackup(string targetDir) + { + foreach (string pictureBackup in Directory.GetFiles(targetDir, "*.jpg.bak").Union(Directory.GetFiles(targetDir, "*.png.bak"))) + { + string picture = pictureBackup.Substring(0, pictureBackup.Length - 4); + if (File.Exists(picture)) + File.Delete(picture); + File.Move(pictureBackup, picture); + } + } } } diff --git a/SpotlightDownloader/Program.cs b/SpotlightDownloader/Program.cs index 1420fe1..35d4a13 100644 --- a/SpotlightDownloader/Program.cs +++ b/SpotlightDownloader/Program.cs @@ -13,7 +13,7 @@ namespace SpotlightDownloader class Program { public const string Name = "SpotlightDL"; - public const string Version = "1.1.1"; + public const string Version = "1.2"; static void Main(string[] args) { @@ -30,6 +30,20 @@ static void Main(string[] args) bool metadata = false; string fromFile = null; + switch (args[0].ToLower()) + { + case "urls": + case "download": + case "wallpaper": + case "lockscreen": + action = args[0].ToLower(); + break; + default: + Console.Error.WriteLine("Unknown action: " + args[0]); + Environment.Exit(1); + break; + } + if (args.Length > 1) { for (int i = 1; i < args.Length; i++) @@ -133,6 +147,29 @@ static void Main(string[] args) Environment.Exit(1); } break; + case "--restore": + if (action == "lockscreen") + { + if (FileSystemAdmin.IsAdmin()) + { + try + { + Lockscreen.RestoreDefaultGlobalLockscreen(); + Environment.Exit(0); + } + catch (Exception e) + { + Console.Error.WriteLine(e.GetType() + ": " + e.Message); + Environment.Exit(4); + } + } + else + { + Console.Error.WriteLine("This program must run as administrator to restore the global lockscreen."); + Environment.Exit(4); + } + } + break; default: Console.Error.WriteLine("Unknown argument: " + args[i]); Environment.Exit(1); @@ -141,20 +178,6 @@ static void Main(string[] args) } } - switch (args[0].ToLower()) - { - case "urls": - case "download": - case "wallpaper": - case "lockscreen": - action = args[0].ToLower(); - break; - default: - Console.Error.WriteLine("Unknown action: " + args[0]); - Environment.Exit(1); - break; - } - try { int downloadCount = 0; @@ -294,6 +317,7 @@ static void Main(string[] args) " --metadata Also save image metadata such as title & copyright as .txt", " --from-file Set the specified file as wallpaper/lockscreen instead of downloading", " --from-dir Set a random image from the specified directory as wallpaper/lockscreen", + " --restore Restore the default lockscreen image, has no effect with other actions", "", "Exit codes:", " 0 Success", diff --git a/SpotlightDownloader/SpotlightDownloader.csproj b/SpotlightDownloader/SpotlightDownloader.csproj index d515a81..607cb7e 100644 --- a/SpotlightDownloader/SpotlightDownloader.csproj +++ b/SpotlightDownloader/SpotlightDownloader.csproj @@ -66,6 +66,7 @@ + @@ -75,6 +76,7 @@ + diff --git a/SpotlightDownloader/WindowsVersion.cs b/SpotlightDownloader/WindowsVersion.cs new file mode 100644 index 0000000..b65dc88 --- /dev/null +++ b/SpotlightDownloader/WindowsVersion.cs @@ -0,0 +1,124 @@ +using Microsoft.Win32; + +namespace SharpTools +{ + /// + /// Retrieve information about the current Windows version + /// + /// + /// Environment.OSVersion does not work with Windows 10. + /// It returns 6.2 which is Windows 8 + /// + /// + /// https://stackoverflow.com/a/37755503 + /// + class WindowsVersion + { + /// + /// Returns the Windows major version number for this computer. + /// + public static uint WinMajorVersion + { + get + { + dynamic major; + // The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10, + // and will most likely (hopefully) be there for some time before MS decides to change this - again... + if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out major)) + { + return (uint) major; + } + + // When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion' + dynamic version; + if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version)) + return 0; + + var versionParts = ((string) version).Split('.'); + if (versionParts.Length != 2) return 0; + uint majorAsUInt; + return uint.TryParse(versionParts[0], out majorAsUInt) ? majorAsUInt : 0; + } + } + + /// + /// Returns the Windows minor version number for this computer. + /// + public static uint WinMinorVersion + { + get + { + dynamic minor; + // The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10, + // and will most likely (hopefully) be there for some time before MS decides to change this - again... + if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber", + out minor)) + { + return (uint) minor; + } + + // When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion' + dynamic version; + if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version)) + return 0; + + var versionParts = ((string) version).Split('.'); + if (versionParts.Length != 2) return 0; + uint minorAsUInt; + return uint.TryParse(versionParts[1], out minorAsUInt) ? minorAsUInt : 0; + } + } + + /// + /// Returns whether or not the current computer is a server or not. + /// + public static uint IsServer + { + get + { + dynamic installationType; + if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallationType", + out installationType)) + { + return (uint) (installationType.Equals("Client") ? 0 : 1); + } + + return 0; + } + } + + /// + /// Returns whether the current computer is running Mono instead of .NET framework (likely Mac and Linux) + /// + public static bool IsMono + { + get + { + return System.Type.GetType("Mono.Runtime") != null; + } + } + + /// + /// Try retrieving a registry key + /// + /// key path + /// Key + /// Value (output) + /// TRUE if successfully retrieved + private static bool TryGetRegistryKey(string path, string key, out dynamic value) + { + value = null; + try + { + var rk = Registry.LocalMachine.OpenSubKey(path); + if (rk == null) return false; + value = rk.GetValue(key); + return value != null; + } + catch + { + return false; + } + } + } +}