diff --git a/src/Squirrel/IUpdateManager.cs b/src/Squirrel/IUpdateManager.cs index 6b38ed49..682f95df 100644 --- a/src/Squirrel/IUpdateManager.cs +++ b/src/Squirrel/IUpdateManager.cs @@ -4,6 +4,7 @@ using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32; namespace Squirrel { @@ -56,6 +57,15 @@ namespace Squirrel /// /// Completion Task FullUninstall(); + + /// + /// Creates an entry in Programs and Features based on the currently + /// applied package + /// + /// The command to run to uninstall, usually update.exe --uninstall + /// The switch for silent uninstall, usually --silent + /// The registry key that was created + Task CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch); } public static class EasyModeMixin diff --git a/src/Squirrel/UpdateManager.cs b/src/Squirrel/UpdateManager.cs index ceb85b18..e2d37d27 100644 --- a/src/Squirrel/UpdateManager.cs +++ b/src/Squirrel/UpdateManager.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Drawing; using System.IO; using System.Linq; using System.Net; +using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32; using NuGet; using Splat; @@ -84,6 +87,64 @@ namespace Squirrel await applyReleases.FullUninstall(); } + const string uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; + public async Task CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch) + { + var releases = ReleaseEntry.ParseReleaseFile(Path.Combine(rootAppDirectory, "packages", "RELEASES")); + var latest = releases.OrderByDescending(x => x.Version).First(); + + // Download the icon and PNG => ICO it. If this doesn't work, who cares + var pkgPath = Path.Combine(rootAppDirectory, "packages", latest.Filename); + var zp = new ZipPackage(pkgPath); + + var targetPng = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png"); + var targetIco = Path.Combine(rootAppDirectory, "app.ico"); + try { + var wc = new WebClient(); + + await wc.DownloadFileTaskAsync(zp.IconUrl, targetPng); + using (var fs = new FileStream(targetIco, FileMode.Create)) + using (var bmp = (Bitmap)Image.FromFile(targetPng)) + using (var ico = Icon.FromHandle(bmp.GetHicon())) { + ico.Save(fs); + } + } catch(Exception ex) { + this.Log().InfoException("Couldn't write uninstall icon, don't care", ex); + } finally { + File.Delete(targetPng); + } + + var stringsToWrite = new[] { + new { Key = "DisplayIcon", Value = "targetIco" }, + new { Key = "DisplayName", Value = zp.Description ?? zp.Summary }, + new { Key = "DisplayVersion", Value = zp.Version.ToString() }, + new { Key = "InstallDate", Value = DateTime.Now.ToString("yyyymmdd") }, + new { Key = "InstallLocation", Value = rootAppDirectory }, + new { Key = "Publisher", Value = zp.Authors.First() }, + new { Key = "QuietUninstallString", Value = String.Format("{0} {1}", uninstallCmd, quietSwitch) }, + new { Key = "UninstallString", Value = uninstallCmd }, + }; + + var dwordsToWrite = new[] { + new { Key = "EstimatedSize", Value = (int)((new FileInfo(pkgPath)).Length / 1024) }, + new { Key = "NoModify", Value = 1 }, + new { Key = "NoRepair", Value = 1 }, + new { Key = "Language", Value = 0x0409 }, + }; + + var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) + .CreateSubKey(uninstallRegSubKey + "\\" + applicationName, RegistryKeyPermissionCheck.ReadWriteSubTree); + + foreach (var kvp in stringsToWrite) { + key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.String); + } + foreach (var kvp in dwordsToWrite) { + key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.DWord); + } + + return key; + } + public void Dispose() { var disp = Interlocked.Exchange(ref updateLock, null);