async Task CheckForUpdates() { try { Trace.TraceInformation("Checking for software updates..."); if (!ApplicationDeployment.IsNetworkDeployed) { Trace.TraceInformation("Not a deployed app. Can't check for updates."); return; } progress.Report(new ProgressModel(UpdateState.Downloading, "Checking for software updates...", -1)); // First, we'll hook up the async handlers before doing the update. // Handle required restart of the app after update ApplicationDeployment.CurrentDeployment.UpdateCompleted += delegate(object sender, AsyncCompletedEventArgs args) { progress.Report(new ProgressModel(UpdateState.Ready, "Update applied. Restart the app.", 100)); Update.IsUpdating = false; if (args.Cancelled) { Trace.TraceWarning("Update was cancelled."); } else if (args.Error != null) { Trace.TraceWarning("Unexpected update error: " + args.Error.Message); } else { try { if (MessageBoxHelper.Show(dispatcher, "Restart Application Required", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes, "You need to restart the app to apply the update. Restart IPFilter now?") != MessageBoxResult.Yes) { return; } dispatcher.Invoke(DispatcherPriority.Normal, new Action(DeploymentHelper.Restart)); } catch (Exception ex) { Trace.TraceError("Exception when restarting app after an update: " + ex); Update.ErrorMessage = "Couldn't restart the app to apply update. It will be updated the next time you start the app."; } } }; // Handle progress of the update. ApplicationDeployment.CurrentDeployment.UpdateProgressChanged += delegate(object sender, DeploymentProgressChangedEventArgs args) { progress.Report(new ProgressModel(UpdateState.Downloading, "Updating application...", args.ProgressPercentage)); Update.IsUpdating = true; Update.DownloadPercentage = args.ProgressPercentage; }; // Do the actual update check var updateAvailable = ApplicationDeployment.CurrentDeployment.CheckForDetailedUpdate(false); Update.IsUpdateAvailable = updateAvailable.UpdateAvailable; if (Update.IsUpdateAvailable) { Update.AvailableVersion = updateAvailable.AvailableVersion; Update.IsUpdateRequired = updateAvailable.IsUpdateRequired; Update.MinimumRequiredVersion = updateAvailable.MinimumRequiredVersion; Update.UpdateSizeBytes = updateAvailable.UpdateSizeBytes; } Trace.TraceInformation("Current version: {0}", Update.CurrentVersion); Trace.TraceInformation("Available version: {0}", Update.AvailableVersion == null ? "<no updates>" : Update.AvailableVersion.ToString()); if (!Update.IsUpdateAvailable) { return; } if (MessageBoxHelper.Show(dispatcher, "Update Available", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes, "An update to version {0} is available. Would you like to update now?", Update.AvailableVersion) != MessageBoxResult.Yes) { return; } Trace.TraceInformation("Starting application update..."); ApplicationDeployment.CurrentDeployment.UpdateAsync(); } catch (Exception ex) { Trace.TraceWarning("Application update check failed: " + ex); } finally { progress.Report(new ProgressModel(UpdateState.Ready, "Ready", 0)); } }
async Task CheckForUpdates() { try { if (Config.Default.settings.update.isDisabled) { Trace.TraceInformation("The software update check is disabled by the user; skipping check"); return; } // Remove any old ClickOnce installs try { var uninstallInfo = UninstallInfo.Find("IPFilter Updater"); if (uninstallInfo != null) { Trace.TraceWarning("Old ClickOnce app installed! Trying to remove..."); var uninstaller = new Uninstaller(); uninstaller.Uninstall(uninstallInfo); Trace.TraceInformation("Successfully removed ClickOnce app"); } } catch (Exception ex) { Trace.TraceError("Failed to remove old ClickOnce app: " + ex); } var applicationDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "IPFilter"); var installerDir = new DirectoryInfo(Path.Combine(applicationDirectory, "installer")); // Detect the current running version. The ProductVersion contains the informational, semantic version e.g. "3.0.0-beta" var versionInfo = Process.GetCurrentProcess().MainModule.FileVersionInfo; var currentVersion = new SemanticVersion(versionInfo.ProductVersion); // Remove any old installers try { if (!installerDir.Exists) { installerDir.Create(); } else if (!Config.Default.settings.update.isCleanupDisabled) { // Don't delete the MSI for the current installed version var currentMsiName = "IPFilter." + currentVersion.ToNormalizedString() + ".msi"; // Scan the directory for all installers foreach (var fileInfo in installerDir.GetFiles("IPFilter.*.msi")) { // Don't remove the installer for the installed version if (fileInfo.Name.Equals(currentMsiName, StringComparison.OrdinalIgnoreCase)) { continue; } Trace.TraceInformation("Removing cached installer: " + fileInfo.Name); fileInfo.SafeDelete(); } } } catch (Exception ex) { Trace.TraceError("Couldn't clean up old installers: " + ex); } Trace.TraceInformation("Checking for software updates..."); progress.Report(new ProgressModel(UpdateState.Downloading, "Checking for software updates...", -1)); var updater = new Updater(); var result = await updater.CheckForUpdateAsync(Config.Default.settings.update.isPreReleaseEnabled); if (result == null) { return; } var latestVersion = new SemanticVersion(result.Version); Update.IsUpdateAvailable = latestVersion > currentVersion; if (Update.IsUpdateAvailable) { Update.AvailableVersion = latestVersion; Update.IsUpdateRequired = true; Update.MinimumRequiredVersion = latestVersion; Update.UpdateSizeBytes = 2000000; } Trace.TraceInformation("Current version: {0}", Update.CurrentVersion); Trace.TraceInformation("Available version: {0}", Update.AvailableVersion?.ToString() ?? "<no updates>"); if (!Update.IsUpdateAvailable) { return; } if (MessageBoxHelper.Show(dispatcher, "Update Available", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes, "An update to version {0} is available. Would you like to update now?", Update.AvailableVersion) != MessageBoxResult.Yes) { return; } Trace.TraceInformation("Starting application update..."); // If we're not "installed", then don't check for updates. This is so the // executable can be stand-alone. Stand-alone self-update to come later. using (var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\IPFilter")) { var installPath = (string)key?.GetValue("InstallPath"); if (installPath == null) { using (var process = new Process()) { process.StartInfo = new ProcessStartInfo("https://www.ipfilter.app/") { UseShellExecute = true }; process.Start(); return; } } } // Download the MSI to the installer directory var msiPath = Path.Combine(installerDir.FullName, "IPFilter." + Update.AvailableVersion + ".msi"); // Download the installer using (var handler = new WebRequestHandler()) { handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; var uri = new Uri($"{result.Uri}?{DateTime.Now.ToString("yyyyMMddHHmmss")}"); using (var httpClient = new HttpClient(handler)) using (var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken.Token)) { if (cancellationToken.IsCancellationRequested) { progress.Report(new ProgressModel(UpdateState.Ready, "Update cancelled. Ready.", 100)); Update.IsUpdating = false; return; } var length = response.Content.Headers.ContentLength; double lengthInMb = !length.HasValue ? -1 : (double)length.Value / 1024 / 1024; double bytesDownloaded = 0; using (var stream = await response.Content.ReadAsStreamAsync()) using (var msi = File.Open(msiPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var buffer = new byte[65535 * 4]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken.Token); while (bytesRead != 0) { await msi.WriteAsync(buffer, 0, bytesRead, cancellationToken.Token); bytesDownloaded += bytesRead; if (length.HasValue) { double downloadedMegs = bytesDownloaded / 1024 / 1024; var percent = (int)Math.Floor((bytesDownloaded / length.Value) * 100); var status = string.Format(CultureInfo.CurrentUICulture, "Downloaded {0:F2} MB of {1:F2} MB", downloadedMegs, lengthInMb); Update.IsUpdating = true; Update.DownloadPercentage = percent; progress.Report(new ProgressModel(UpdateState.Downloading, status, percent)); } if (cancellationToken.IsCancellationRequested) { progress.Report(new ProgressModel(UpdateState.Ready, "Update cancelled. Ready.", 100)); Update.IsUpdating = false; return; } bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken.Token); } } } } progress.Report(new ProgressModel(UpdateState.Ready, "Launching update...", 100)); Update.IsUpdating = false; // Now run the installer var sb = new StringBuilder("msiexec.exe "); // Enable logging for the installer var installLog = Path.Combine(applicationDirectory, "install.log"); sb.AppendFormat(" /l*v \"{0}\"", installLog); sb.AppendFormat(" /i \"{0}\"", msiPath); //sb.Append(" /passive"); ProcessInformation processInformation = new ProcessInformation(); StartupInfo startupInfo = new StartupInfo(); SecurityAttributes processSecurity = new SecurityAttributes(); SecurityAttributes threadSecurity = new SecurityAttributes(); processSecurity.nLength = Marshal.SizeOf(processSecurity); threadSecurity.nLength = Marshal.SizeOf(threadSecurity); const int NormalPriorityClass = 0x0020; if (!ProcessManager.CreateProcess(null, sb, processSecurity, threadSecurity, false, NormalPriorityClass, IntPtr.Zero, null, startupInfo, processInformation)) { throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); } try { //dispatcher.Invoke(DispatcherPriority.Normal, new Action(Application.Current.Shutdown)); Application.Current.Shutdown(); } catch (Exception ex) { Trace.TraceError("Exception when shutting down app for update: " + ex); Update.ErrorMessage = "Couldn't shutdown the app to apply update."; } } catch (Exception ex) { Trace.TraceWarning("Application update check failed: " + ex); } finally { progress.Report(new ProgressModel(UpdateState.Ready, "Ready", 0)); } }
async Task CheckForUpdates() { try { // Remove any old ClickOnce installs try { var uninstallInfo = UninstallInfo.Find("IPFilter Updater"); if (uninstallInfo != null) { Trace.TraceWarning("Old ClickOnce app installed! Trying to remove..."); var uninstaller = new Uninstaller(); uninstaller.Uninstall(uninstallInfo); Trace.TraceInformation("Successfully removed ClickOnce app"); } } catch (Exception ex) { Trace.TraceError("Failed to remove old ClickOnce app: " + ex); telemetryClient?.TrackException(ex); } Trace.TraceInformation("Checking for software updates..."); progress.Report(new ProgressModel(UpdateState.Downloading, "Checking for software updates...", -1)); var updater = new Updater(); var result = await updater.CheckForUpdateAsync(); var currentVersion = new Version(Process.GetCurrentProcess().MainModule.FileVersionInfo.FileVersion); var latestVersion = new Version(result.Version); Update.IsUpdateAvailable = latestVersion > currentVersion; if (Update.IsUpdateAvailable) { Update.AvailableVersion = latestVersion; Update.IsUpdateRequired = true; Update.MinimumRequiredVersion = latestVersion; Update.UpdateSizeBytes = 2000000; } Trace.TraceInformation("Current version: {0}", Update.CurrentVersion); Trace.TraceInformation("Available version: {0}", Update.AvailableVersion?.ToString() ?? "<no updates>"); if (!Update.IsUpdateAvailable) { return; } if (MessageBoxHelper.Show(dispatcher, "Update Available", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes, "An update to version {0} is available. Would you like to update now?", Update.AvailableVersion) != MessageBoxResult.Yes) { return; } Trace.TraceInformation("Starting application update..."); // If we're not "installed", then don't check for updates. This is so the // executable can be stand-alone. Stand-alone self-update to come later. using (var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\IPFilter")) { var installPath = (string)key?.GetValue("InstallPath"); if (installPath == null) { using (var process = new Process()) { process.StartInfo = new ProcessStartInfo("https://davidmoore.github.io/ipfilter/") { UseShellExecute = true }; process.Start(); return; } } } var msiPath = Path.Combine(Path.GetTempPath(), "IPFilter.msi"); // Download the installer using (var handler = new WebRequestHandler()) { handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; var uri = new Uri($"{result.Uri}?{DateTime.Now.ToString("yyyyMMddHHmmss")}"); using (var httpClient = new HttpClient(handler)) using (var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken.Token)) { if (cancellationToken.IsCancellationRequested) { progress.Report(new ProgressModel(UpdateState.Ready, "Update cancelled. Ready.", 100)); Update.IsUpdating = false; return; } var length = response.Content.Headers.ContentLength; double lengthInMb = !length.HasValue ? -1 : (double)length.Value / 1024 / 1024; double bytesDownloaded = 0; using (var stream = await response.Content.ReadAsStreamAsync()) using (var msi = File.Open(msiPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var buffer = new byte[65535 * 4]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken.Token); while (bytesRead != 0) { await msi.WriteAsync(buffer, 0, bytesRead, cancellationToken.Token); bytesDownloaded += bytesRead; if (length.HasValue) { double downloadedMegs = bytesDownloaded / 1024 / 1024; var percent = (int)Math.Floor((bytesDownloaded / length.Value) * 100); var status = string.Format(CultureInfo.CurrentUICulture, "Downloaded {0:F2} MB of {1:F2} MB", downloadedMegs, lengthInMb); Update.IsUpdating = true; Update.DownloadPercentage = percent; progress.Report(new ProgressModel(UpdateState.Downloading, status, percent)); } if (cancellationToken.IsCancellationRequested) { progress.Report(new ProgressModel(UpdateState.Ready, "Update cancelled. Ready.", 100)); Update.IsUpdating = false; return; } bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken.Token); } } } } progress.Report(new ProgressModel(UpdateState.Ready, "Launching update...", 100)); Update.IsUpdating = false; // Now run the installer var sb = new StringBuilder("msiexec.exe "); // Enable logging for the installer sb.AppendFormat(" /l*v \"{0}\"", Path.Combine(Path.GetTempPath(), "IPFilter.log")); sb.AppendFormat(" /i \"{0}\"", msiPath); //sb.Append(" /passive"); ProcessInformation processInformation = new ProcessInformation(); StartupInfo startupInfo = new StartupInfo(); SecurityAttributes processSecurity = new SecurityAttributes(); SecurityAttributes threadSecurity = new SecurityAttributes(); processSecurity.nLength = Marshal.SizeOf(processSecurity); threadSecurity.nLength = Marshal.SizeOf(threadSecurity); const int NormalPriorityClass = 0x0020; if (!ProcessManager.CreateProcess(null, sb, processSecurity, threadSecurity, false, NormalPriorityClass, IntPtr.Zero, null, startupInfo, processInformation)) { throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); } try { //dispatcher.Invoke(DispatcherPriority.Normal, new Action(Application.Current.Shutdown)); Application.Current.Shutdown(); } catch (Exception ex) { Trace.TraceError("Exception when shutting down app for update: " + ex); Update.ErrorMessage = "Couldn't shutdown the app to apply update."; telemetryClient?.TrackException(ex); } } catch (Exception ex) { Trace.TraceWarning("Application update check failed: " + ex); telemetryClient?.TrackException(ex); telemetryClient?.Flush(); } finally { progress.Report(new ProgressModel(UpdateState.Ready, "Ready", 0)); } }