diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 0ac9866bf2..2a8b86659d 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -218,74 +218,6 @@ static async Task GetLatestVersionAsync() return latestAvailableVersion; } - sealed class AvailableVersionInfo - { - public Version Version; - public string DownloadUrl; - } - - sealed class UpdateSettings : INotifyPropertyChanged - { - public UpdateSettings(ILSpySettings spySettings) - { - XElement s = spySettings["UpdateSettings"]; - this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true; - try - { - this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck"); - } - catch (FormatException) - { - // avoid crashing on settings files invalid due to - // https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2 - } - } - - bool automaticUpdateCheckEnabled; - - public bool AutomaticUpdateCheckEnabled { - get { return automaticUpdateCheckEnabled; } - set { - if (automaticUpdateCheckEnabled != value) - { - automaticUpdateCheckEnabled = value; - Save(); - OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled)); - } - } - } - - DateTime? lastSuccessfulUpdateCheck; - - public DateTime? LastSuccessfulUpdateCheck { - get { return lastSuccessfulUpdateCheck; } - set { - if (lastSuccessfulUpdateCheck != value) - { - lastSuccessfulUpdateCheck = value; - Save(); - OnPropertyChanged(nameof(LastSuccessfulUpdateCheck)); - } - } - } - - public void Save() - { - XElement updateSettings = new XElement("UpdateSettings"); - updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled)); - if (lastSuccessfulUpdateCheck != null) - updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck)); - ILSpySettings.SaveSettings(updateSettings); - } - - public event PropertyChangedEventHandler PropertyChanged; - - void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } - /// /// If automatic update checking is enabled, checks if there are any updates available. /// Returns the download URL if an update is available. @@ -349,4 +281,73 @@ public interface IAboutPageAddition { void Write(ISmartTextOutput textOutput); } + + sealed class AvailableVersionInfo + { + public Version Version; + public string DownloadUrl; + } + + sealed class UpdateSettings : INotifyPropertyChanged + { + public UpdateSettings(ILSpySettings spySettings) + { + XElement s = spySettings["UpdateSettings"]; + this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true; + try + { + this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck"); + } + catch (FormatException) + { + // avoid crashing on settings files invalid due to + // https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2 + } + } + + bool automaticUpdateCheckEnabled; + + public bool AutomaticUpdateCheckEnabled { + get { return automaticUpdateCheckEnabled; } + set { + if (automaticUpdateCheckEnabled != value) + { + automaticUpdateCheckEnabled = value; + Save(); + OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled)); + } + } + } + + DateTime? lastSuccessfulUpdateCheck; + + public DateTime? LastSuccessfulUpdateCheck { + get { return lastSuccessfulUpdateCheck; } + set { + if (lastSuccessfulUpdateCheck != value) + { + lastSuccessfulUpdateCheck = value; + Save(); + OnPropertyChanged(nameof(LastSuccessfulUpdateCheck)); + } + } + } + + public void Save() + { + XElement updateSettings = new XElement("UpdateSettings"); + updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled)); + if (lastSuccessfulUpdateCheck != null) + updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck)); + ILSpySettings.SaveSettings(updateSettings); + } + + public event PropertyChangedEventHandler PropertyChanged; + + void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index d33163bad0..33ef1b9eff 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -45,6 +45,7 @@ + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index eaa65475ba..ec09383a28 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -25,6 +25,7 @@ using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; @@ -57,6 +58,12 @@ using Microsoft.Win32; +using NetSparkleUpdater; +using NetSparkleUpdater.Configurations; +using NetSparkleUpdater.Enums; +using NetSparkleUpdater.SignatureVerifiers; +using NetSparkleUpdater.UI.WPF; + namespace ICSharpCode.ILSpy { /// @@ -72,6 +79,8 @@ partial class MainWindow : Window AssemblyList assemblyList; AssemblyListTreeNode assemblyListTreeNode; + SparkleUpdater _sparkle; + static MainWindow instance; public static MainWindow Instance { @@ -895,6 +904,66 @@ void MainWindow_Loaded(object sender, RoutedEventArgs e) } Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(spySettings))); + + + var s = new UpdateSettings(spySettings); + bool automaticCheckingEnabled = s.AutomaticUpdateCheckEnabled; + + // TODO: That would need to be way more sophisticated - we ship zip, msi, vsix (actually doing an update-check there is wrong even today) + // Multiple of those could be installed on the same machine in multiple versions (esp. zip) + // Somehow we'd need to "tag" the version running from our msi installer, and only then offering auto-update (eg MsiInstaller.txt marker file) + // And then have an IUpdateService with one impl existing plus one for auto-updating + +#if DEBUG + // automaticCheckingEnabled = false; +#endif + // TODO: Additional check if branch version + + if (automaticCheckingEnabled) + EnableSparkleUpdateChecking(); + } + + void EnableSparkleUpdateChecking() + { + // We'd need two appcast.xml's, one for x64 and one for arm64 + if (RuntimeInformation.ProcessArchitecture != Architecture.X64) + return; + + // This should update with UpdateTheme + bool isLightTheme = ThemeManager.Current.Theme.Contains("light", StringComparison.InvariantCultureIgnoreCase); + + var extraHeadAdditionForReleaseNotes = ""; + + // https://github.com/NetSparkleUpdater/NetSparkle/blob/develop/src/NetSparkle.Samples.NetCore.WPF/MainWindow.xaml.cs + string sparkleSettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ICSharpCode\\ILSpy-sparkle.json"); + + _sparkle = new SparkleUpdater( + "https://ilspy.net/appcast.xml", + new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=") + ) { + UIFactory = new UIFactory(Images.ILSpyIcon) { + UseStaticUpdateWindowBackgroundColor = false, + AdditionalReleaseNotesHeaderHTML = extraHeadAdditionForReleaseNotes, + ProcessWindowAfterInit = (w, f) => { + w.Style = (Style)Application.Current.FindResource("DialogWindow"); + } + }, + ShowsUIOnMainThread = true, + RelaunchAfterUpdate = false, + CustomInstallerArguments = "", + Configuration = new JSONConfiguration(new NetSparkleUpdater.AssemblyAccessors.AssemblyReflectionAccessor(null), sparkleSettingsPath) + }; + + _sparkle.StartLoop(true); } void OpenAssemblies(ILSpySettings spySettings) @@ -951,6 +1020,9 @@ internal static bool FormatExceptions(App.ExceptionData[] exceptions, StringBuil public async Task ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false) { + // TODO: Properly replace old code-paths, this is for UI testing of Netsparkle only + var info = await _sparkle.CheckForUpdatesAtUserRequest(); + string downloadUrl; if (forceCheck) {