diff --git a/AutoUpdater.NET/AutoUpdater.NET.csproj b/AutoUpdater.NET/AutoUpdater.NET.csproj index 65946da..d4920bc 100644 --- a/AutoUpdater.NET/AutoUpdater.NET.csproj +++ b/AutoUpdater.NET/AutoUpdater.NET.csproj @@ -29,7 +29,7 @@ https://github.com/ravibpatel/AutoUpdater.NET/releases build $(OutputPath)\$(Configuration)\AutoUpdater.NET.xml - default + latest build\lib @@ -51,6 +51,6 @@ - + \ No newline at end of file diff --git a/AutoUpdater.NET/AutoUpdater.NET.csproj.DotSettings b/AutoUpdater.NET/AutoUpdater.NET.csproj.DotSettings deleted file mode 100644 index b9fd6ee..0000000 --- a/AutoUpdater.NET/AutoUpdater.NET.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp80 \ No newline at end of file diff --git a/AutoUpdater.NET/AutoUpdater.cs b/AutoUpdater.NET/AutoUpdater.cs index 00a7d92..90a5115 100644 --- a/AutoUpdater.NET/AutoUpdater.cs +++ b/AutoUpdater.NET/AutoUpdater.cs @@ -85,6 +85,11 @@ public static class AutoUpdater /// public static string InstallationPath; + /// + /// If you are using a zip file as an update file, then you can set this value to a new executable path relative to the installation directory. + /// + public static string ExecutablePath; + /// /// Set the Application Title shown in Update dialog. Although AutoUpdater.NET will get it automatically, you can set this property if you like to give custom Title. /// @@ -272,73 +277,71 @@ public static void Start(string appCast, Assembly myAssembly = null) _remindLaterTimer = null; } - if (!Running && _remindLaterTimer == null) - { - Running = true; + if (Running || _remindLaterTimer != null) return; - AppCastURL = appCast; + Running = true; - _isWinFormsApplication = Application.MessageLoop; + AppCastURL = appCast; - if (!_isWinFormsApplication) - { - Application.EnableVisualStyles(); - } + _isWinFormsApplication = Application.MessageLoop; - Assembly assembly = myAssembly ?? Assembly.GetEntryAssembly(); + if (!_isWinFormsApplication) + { + Application.EnableVisualStyles(); + } - if (Synchronous) - { - try - { - var result = CheckUpdate(assembly); + Assembly assembly = myAssembly ?? Assembly.GetEntryAssembly(); - if (StartUpdate(result)) - { - return; - } + if (Synchronous) + { + try + { + var result = CheckUpdate(assembly); - Running = false; - } - catch (Exception exception) + if (StartUpdate(result)) { - ShowError(exception); + return; } + + Running = false; } - else + catch (Exception exception) { - using (var backgroundWorker = new BackgroundWorker()) - { - backgroundWorker.DoWork += (_, args) => - { - Assembly mainAssembly = args.Argument as Assembly; + ShowError(exception); + } + } + else + { + using var backgroundWorker = new BackgroundWorker(); + + backgroundWorker.DoWork += (_, args) => + { + Assembly mainAssembly = args.Argument as Assembly; - args.Result = CheckUpdate(mainAssembly); - }; + args.Result = CheckUpdate(mainAssembly); + }; - backgroundWorker.RunWorkerCompleted += (_, args) => + backgroundWorker.RunWorkerCompleted += (_, args) => + { + if (args.Error != null) + { + ShowError(args.Error); + } + else + { + if (!args.Cancelled) { - if (args.Error != null) - { - ShowError(args.Error); - } - else + if (StartUpdate(args.Result)) { - if (!args.Cancelled) - { - if (StartUpdate(args.Result)) - { - return; - } - } - - Running = false; + return; } - }; + } - backgroundWorker.RunWorkerAsync(assembly); + Running = false; } - } + }; + + backgroundWorker.RunWorkerAsync(assembly); } } @@ -446,47 +449,46 @@ private static bool StartUpdate(object result) } else { - if (result is UpdateInfoEventArgs args) + if (result is not UpdateInfoEventArgs args) return false; + + if (CheckForUpdateEvent != null) { - if (CheckForUpdateEvent != null) - { - CheckForUpdateEvent(args); - } - else + CheckForUpdateEvent(args); + } + else + { + if (args.IsUpdateAvailable) { - if (args.IsUpdateAvailable) + if (Mandatory && UpdateMode == Mode.ForcedDownload) { - if (Mandatory && UpdateMode == Mode.ForcedDownload) + DownloadUpdate(args); + Exit(); + } + else + { + if (Thread.CurrentThread.GetApartmentState().Equals(ApartmentState.STA)) { - DownloadUpdate(args); - Exit(); + ShowUpdateForm(args); } else { - if (Thread.CurrentThread.GetApartmentState().Equals(ApartmentState.STA)) - { - ShowUpdateForm(args); - } - else - { - Thread thread = new Thread(new ThreadStart(delegate { ShowUpdateForm(args); })); - thread.CurrentCulture = - thread.CurrentUICulture = CultureInfo.CurrentCulture; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } + Thread thread = new Thread(new ThreadStart(delegate { ShowUpdateForm(args); })); + thread.CurrentCulture = + thread.CurrentUICulture = CultureInfo.CurrentCulture; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); } - - return true; } - if (ReportErrors) - { - MessageBox.Show(Resources.UpdateUnavailableMessage, - Resources.UpdateUnavailableCaption, - MessageBoxButtons.OK, MessageBoxIcon.Information); - } + return true; + } + + if (ReportErrors) + { + MessageBox.Show(Resources.UpdateUnavailableMessage, + Resources.UpdateUnavailableCaption, + MessageBoxButtons.OK, MessageBoxIcon.Information); } } } @@ -553,7 +555,7 @@ internal static void Exit() if (!process.HasExited) { - process.Kill(); //TODO show UI message asking user to close program himself instead of silently killing it + process.Kill(); //TODO: Show UI message asking user to close program himself instead of silently killing it } } } @@ -637,15 +639,14 @@ internal static void SetTimer(DateTime remindLater) /// public static bool DownloadUpdate(UpdateInfoEventArgs args) { - using (var downloadDialog = new DownloadUpdateDialog(args)) + using var downloadDialog = new DownloadUpdateDialog(args); + + try + { + return downloadDialog.ShowDialog().Equals(DialogResult.OK); + } + catch (TargetInvocationException) { - try - { - return downloadDialog.ShowDialog().Equals(DialogResult.OK); - } - catch (TargetInvocationException) - { - } } return false; @@ -656,17 +657,16 @@ public static bool DownloadUpdate(UpdateInfoEventArgs args) /// public static void ShowUpdateForm(UpdateInfoEventArgs args) { - using (var updateForm = new UpdateForm(args)) + using var updateForm = new UpdateForm(args); + + if (UpdateFormSize.HasValue) { - if (UpdateFormSize.HasValue) - { - updateForm.Size = UpdateFormSize.Value; - } + updateForm.Size = UpdateFormSize.Value; + } - if (updateForm.ShowDialog().Equals(DialogResult.OK)) - { - Exit(); - } + if (updateForm.ShowDialog().Equals(DialogResult.OK)) + { + Exit(); } } diff --git a/AutoUpdater.NET/DownloadUpdateDialog.cs b/AutoUpdater.NET/DownloadUpdateDialog.cs index 71de3e4..c29b016 100644 --- a/AutoUpdater.NET/DownloadUpdateDialog.cs +++ b/AutoUpdater.NET/DownloadUpdateDialog.cs @@ -62,7 +62,7 @@ private void DownloadUpdateDialogLoad(object sender, EventArgs e) private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { - if (_startedAt == default(DateTime)) + if (_startedAt == default) { _startedAt = DateTime.Now; } @@ -84,10 +84,7 @@ private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEve private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEventArgs asyncCompletedEventArgs) { - if (asyncCompletedEventArgs.Cancelled) - { - return; - } + if (asyncCompletedEventArgs.Cancelled) return; try { @@ -107,7 +104,8 @@ private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEvent { try { - contentDisposition = new ContentDisposition(_webClient.ResponseHeaders["Content-Disposition"]); + contentDisposition = + new ContentDisposition(_webClient.ResponseHeaders["Content-Disposition"]); } catch (FormatException) { @@ -127,7 +125,9 @@ private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEvent var tempPath = Path.Combine( - string.IsNullOrEmpty(AutoUpdater.DownloadPath) ? Path.GetTempPath() : AutoUpdater.DownloadPath, + string.IsNullOrEmpty(AutoUpdater.DownloadPath) + ? Path.GetTempPath() + : AutoUpdater.DownloadPath, fileName); if (File.Exists(tempPath)) @@ -154,7 +154,9 @@ private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEvent var extension = Path.GetExtension(tempPath); if (extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)) { - string installerPath = Path.Combine(Path.GetDirectoryName(tempPath) ?? throw new InvalidOperationException(), "ZipExtractor.exe"); + string installerPath = + Path.Combine(Path.GetDirectoryName(tempPath) ?? throw new InvalidOperationException(), + "ZipExtractor.exe"); File.WriteAllBytes(installerPath, Resources.ZipExtractor); @@ -162,20 +164,32 @@ private void WebClientOnDownloadFileCompleted(object sender, AsyncCompletedEvent string updatedExe = _args.ExecutablePath; string extractionPath = Path.GetDirectoryName(currentExe); - if (!string.IsNullOrEmpty(AutoUpdater.InstallationPath) && + if (string.IsNullOrWhiteSpace(updatedExe) && + !string.IsNullOrWhiteSpace(AutoUpdater.ExecutablePath)) + { + updatedExe = AutoUpdater.ExecutablePath; + } + + if (!string.IsNullOrWhiteSpace(AutoUpdater.InstallationPath) && Directory.Exists(AutoUpdater.InstallationPath)) { extractionPath = AutoUpdater.InstallationPath; } StringBuilder arguments = - new StringBuilder($"--input \"{tempPath}\" --output \"{extractionPath}\" --current-exe \"{currentExe}\" --updated-exe \"{updatedExe}\""); + new StringBuilder( + $"--input \"{tempPath}\" --output \"{extractionPath}\" --current-exe \"{currentExe}\""); + + if (!string.IsNullOrWhiteSpace(updatedExe)) + { + arguments.Append($" --updated-exe \"{updatedExe}\""); + } if (AutoUpdater.ClearAppDirectory) { arguments.Append(" --clear"); } - + string[] args = Environment.GetCommandLineArgs(); for (int i = 1; i < args.Length; i++) { @@ -255,25 +269,19 @@ private static string BytesToString(long byteCount) private static void CompareChecksum(string fileName, CheckSum checksum) { - using (var hashAlgorithm = + using var hashAlgorithm = HashAlgorithm.Create( - string.IsNullOrEmpty(checksum.HashingAlgorithm) ? "MD5" : checksum.HashingAlgorithm)) - { - using (var stream = File.OpenRead(fileName)) - { - if (hashAlgorithm != null) - { - var hash = hashAlgorithm.ComputeHash(stream); - var fileChecksum = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); + string.IsNullOrEmpty(checksum.HashingAlgorithm) ? "MD5" : checksum.HashingAlgorithm); + using var stream = File.OpenRead(fileName); - if (fileChecksum == checksum.Value.ToLower()) return; + if (hashAlgorithm == null) throw new Exception(Resources.HashAlgorithmNotSupportedMessage); - throw new Exception(Resources.FileIntegrityCheckFailedMessage); - } + var hash = hashAlgorithm.ComputeHash(stream); + var fileChecksum = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant(); - throw new Exception(Resources.HashAlgorithmNotSupportedMessage); - } - } + if (fileChecksum == checksum.Value.ToLower()) return; + + throw new Exception(Resources.FileIntegrityCheckFailedMessage); } private void DownloadUpdateDialog_FormClosing(object sender, FormClosingEventArgs e) diff --git a/AutoUpdater.NET/UpdateForm.Designer.cs b/AutoUpdater.NET/UpdateForm.Designer.cs index e56d1b5..3d84968 100644 --- a/AutoUpdater.NET/UpdateForm.Designer.cs +++ b/AutoUpdater.NET/UpdateForm.Designer.cs @@ -2,7 +2,7 @@ namespace AutoUpdaterDotNET { - partial class UpdateForm + sealed partial class UpdateForm { /// /// Required designer variable. diff --git a/AutoUpdater.NET/UpdateForm.cs b/AutoUpdater.NET/UpdateForm.cs index c10d73e..07c7440 100644 --- a/AutoUpdater.NET/UpdateForm.cs +++ b/AutoUpdater.NET/UpdateForm.cs @@ -9,7 +9,7 @@ namespace AutoUpdaterDotNET { - internal partial class UpdateForm : Form + internal sealed partial class UpdateForm : Form { private readonly UpdateInfoEventArgs _args; @@ -21,12 +21,12 @@ public UpdateForm(UpdateInfoEventArgs args) buttonSkip.Visible = AutoUpdater.ShowSkipButton; buttonRemindLater.Visible = AutoUpdater.ShowRemindLaterButton; var resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateForm)); - Text = string.Format(resources.GetString("$this.Text", CultureInfo.CurrentCulture), + Text = string.Format(resources.GetString("$this.Text", CultureInfo.CurrentCulture)!, AutoUpdater.AppTitle, _args.CurrentVersion); - labelUpdate.Text = string.Format(resources.GetString("labelUpdate.Text", CultureInfo.CurrentCulture), + labelUpdate.Text = string.Format(resources.GetString("labelUpdate.Text", CultureInfo.CurrentCulture)!, AutoUpdater.AppTitle); labelDescription.Text = - string.Format(resources.GetString("labelDescription.Text", CultureInfo.CurrentCulture), + string.Format(resources.GetString("labelDescription.Text", CultureInfo.CurrentCulture)!, AutoUpdater.AppTitle, _args.CurrentVersion, _args.InstalledVersion); if (AutoUpdater.Mandatory && AutoUpdater.UpdateMode == Mode.Forced) @@ -117,45 +117,34 @@ private void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebV private void UseLatestIE() { - int ieValue = 0; - switch (webBrowser.Version.Major) + int ieValue = webBrowser.Version.Major switch { - case 11: - ieValue = 11001; - break; - case 10: - ieValue = 10001; - break; - case 9: - ieValue = 9999; - break; - case 8: - ieValue = 8888; - break; - case 7: - ieValue = 7000; - break; - } + 11 => 11001, + 10 => 10001, + 9 => 9999, + 8 => 8888, + 7 => 7000, + _ => 0 + }; + + if (ieValue == 0) return; - if (ieValue != 0) + try { - try + using (RegistryKey registryKey = + Registry.CurrentUser.OpenSubKey( + @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", + true)) { - using (RegistryKey registryKey = - Registry.CurrentUser.OpenSubKey( - @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", - true)) - { - registryKey?.SetValue(Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName), - ieValue, - RegistryValueKind.DWord); - } - } - catch (Exception) - { - // ignored + registryKey?.SetValue(Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName), + ieValue, + RegistryValueKind.DWord); } } + catch (Exception) + { + // ignored + } } private void UpdateFormLoad(object sender, EventArgs e) @@ -192,42 +181,32 @@ private void ButtonRemindLaterClick(object sender, EventArgs e) { if (AutoUpdater.LetUserSelectRemindLater) { - using (var remindLaterForm = new RemindLaterForm()) - { - var dialogResult = remindLaterForm.ShowDialog(); + using var remindLaterForm = new RemindLaterForm(); + var dialogResult = remindLaterForm.ShowDialog(); - if (dialogResult.Equals(DialogResult.OK)) - { + switch (dialogResult) + { + case DialogResult.OK: AutoUpdater.RemindLaterTimeSpan = remindLaterForm.RemindLaterFormat; AutoUpdater.RemindLaterAt = remindLaterForm.RemindLaterAt; - } - else if (dialogResult.Equals(DialogResult.Abort)) - { + break; + case DialogResult.Abort: ButtonUpdateClick(sender, e); return; - } - else - { + default: return; - } } } AutoUpdater.PersistenceProvider.SetSkippedVersion(null); - DateTime remindLaterDateTime = DateTime.Now; - switch (AutoUpdater.RemindLaterTimeSpan) + DateTime remindLaterDateTime = AutoUpdater.RemindLaterTimeSpan switch { - case RemindLaterFormat.Days: - remindLaterDateTime = DateTime.Now + TimeSpan.FromDays(AutoUpdater.RemindLaterAt); - break; - case RemindLaterFormat.Hours: - remindLaterDateTime = DateTime.Now + TimeSpan.FromHours(AutoUpdater.RemindLaterAt); - break; - case RemindLaterFormat.Minutes: - remindLaterDateTime = DateTime.Now + TimeSpan.FromMinutes(AutoUpdater.RemindLaterAt); - break; - } + RemindLaterFormat.Days => DateTime.Now + TimeSpan.FromDays(AutoUpdater.RemindLaterAt), + RemindLaterFormat.Hours => DateTime.Now + TimeSpan.FromHours(AutoUpdater.RemindLaterAt), + RemindLaterFormat.Minutes => DateTime.Now + TimeSpan.FromMinutes(AutoUpdater.RemindLaterAt), + _ => DateTime.Now + }; AutoUpdater.PersistenceProvider.SetRemindLater(remindLaterDateTime); AutoUpdater.SetTimer(remindLaterDateTime); diff --git a/AutoUpdater.NET/build/Autoupdater.NET.Official.nuspec b/AutoUpdater.NET/build/Autoupdater.NET.Official.nuspec index 79013d8..645c77b 100644 --- a/AutoUpdater.NET/build/Autoupdater.NET.Official.nuspec +++ b/AutoUpdater.NET/build/Autoupdater.NET.Official.nuspec @@ -15,16 +15,16 @@ autoupdate updater c# vb wpf winforms - + - + - + - + diff --git a/AutoUpdaterTest/FormMain.cs b/AutoUpdaterTest/FormMain.cs index 26a6bc4..5c88085 100644 --- a/AutoUpdaterTest/FormMain.cs +++ b/AutoUpdaterTest/FormMain.cs @@ -35,7 +35,7 @@ private void FormMain_Load(object sender, EventArgs e) //Uncomment below line to see russian version - //Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("ru"); + Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("hi"); //If you want to open download page when user click on download button uncomment below line. @@ -245,8 +245,9 @@ private void ButtonCheckForUpdate_Click(object sender, EventArgs e) // AutoUpdater.Start("https://rbsoft.org/updates/AutoUpdaterTest.xml"); //} - AutoUpdater.ClearAppDirectory = false; - AutoUpdater.Start("file://///rudra-pc/share/test.xml"); + AutoUpdater.ExecutablePath = "bin/AutoUpdaterTest.exe"; + AutoUpdater.ClearAppDirectory = true; + AutoUpdater.Start("https://rbsoft.org/updates/AutoUpdaterTest.xml"); } } } \ No newline at end of file diff --git a/README.md b/README.md index 026f1a5..61497e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ [![Build status](https://ci.appveyor.com/api/projects/status/yng987o7dauk9gqc?svg=true)](https://ci.appveyor.com/project/ravibpatel/autoupdater-net) -AutoUpdater.NET is a class library that allows .NET developers to easily add auto update functionality to their classic desktop application projects. +AutoUpdater.NET is a class library that allows .NET developers to easily add auto update functionality to their classic +desktop application projects. ## The NuGet Package [![NuGet](https://img.shields.io/nuget/v/Autoupdater.NET.Official.svg)](https://www.nuget.org/packages/Autoupdater.NET.Official/) [![NuGet](https://img.shields.io/nuget/dt/Autoupdater.NET.Official.svg)](https://www.nuget.org/packages/Autoupdater.NET.Official/) @@ -20,13 +21,20 @@ This library only works for WinForms or WPF application projects. ## How it works -AutoUpdater.NET downloads the XML file containing update information from your server. It uses this XML file to get the information about the latest version of the software. If the latest version of the software is greater than the current version of the software installed on User's PC then AutoUpdater.NET shows update dialog to the user. If user press the update button to update the software then It downloads the update file (Installer) from URL provided in XML file and executes the installer file it just downloaded. It is a job of installer after this point to carry out the update. If you provide zip file URL instead of installer then AutoUpdater.NET will extract the contents of zip file to application directory. +AutoUpdater.NET downloads the XML file containing update information from your server. It uses this XML file to get the +information about the latest version of the software. If the latest version of the software is greater than the current +version of the software installed on User's PC then AutoUpdater.NET shows update dialog to the user. If user press the +update button to update the software then It downloads the update file (Installer) from URL provided in XML file and +executes the installer file it just downloaded. It is a job of installer after this point to carry out the update. If +you provide zip file URL instead of installer then AutoUpdater.NET will extract the contents of zip file to application +directory. ## Using the code ### XML file -AutoUpdater.NET uses XML file located on a server to get the release information about the latest version of the software. You need to create XML file like below and then you need to upload it to your server. +AutoUpdater.NET uses XML file located on a server to get the release information about the latest version of the +software. You need to create XML file like below and then you need to upload it to your server. ````xml @@ -40,46 +48,66 @@ AutoUpdater.NET uses XML file located on a server to get the release information There are two things you need to provide in XML file as you can see above. -* version (Required) : You need to provide latest version of the application between version tags. Version should be in X.X.X.X format. -* url (Required): You need to provide URL of the latest version installer file or zip file between url tags. AutoUpdater.NET downloads the file provided here and install it when user press the Update button. -* changelog (Optional): You need to provide URL of the change log of your application between changelog tags. If you don't provide the URL of the changelog then update dialog won't show the change log. -* mandatory (Optional): You can set this to true if you don't want user to skip this version. This will ignore Remind Later and Skip options and hide both Skip and Remind Later button on update dialog. - * mode (Attribute, Optional): You can provide mode attribute on mandatory element to change the behaviour of the mandatory flag. If you provide "1" as the value of mode attribute then it will also hide the Close button on update dialog. If you provide "2" as the value of mode attribute then it will skip the update dialog and start downloading and updating application automatically. +* version (Required) : You need to provide latest version of the application between version tags. Version should be in + X.X.X.X format. +* url (Required): You need to provide URL of the latest version installer file or zip file between url tags. + AutoUpdater.NET downloads the file provided here and install it when user press the Update button. +* changelog (Optional): You need to provide URL of the change log of your application between changelog tags. If you + don't provide the URL of the changelog then update dialog won't show the change log. +* mandatory (Optional): You can set this to true if you don't want user to skip this version. This will ignore Remind + Later and Skip options and hide both Skip and Remind Later button on update dialog. + * mode (Attribute, Optional): You can provide mode attribute on mandatory element to change the behaviour of the + mandatory flag. If you provide "1" as the value of mode attribute then it will also hide the Close button on + update dialog. If you provide "2" as the value of mode attribute then it will skip the update dialog and start + downloading and updating application automatically. ````xml true ```` - * minVersion (Attribute, Optional): You can also provide minVersion attribute on mandatory element. When you provide it, Mandatory option will be triggered only if the installed version of the app is less than the minimum version you specified here. + * minVersion (Attribute, Optional): You can also provide minVersion attribute on mandatory element. When you provide + it, Mandatory option will be triggered only if the installed version of the app is less than the minimum version + you specified here. ````xml true ```` -* executable (Optional): You can provide the path of the executable if it was changed in the update. It should be relative to the installation directory of the application. For example, if the new executable is located inside the bin folder of the installation directory, then you should provide it as shown below. +* executable (Optional): You can provide the path of the executable if it was changed in the update. It should be + relative to the installation directory of the application. For example, if the new executable is located inside the + bin folder of the installation directory, then you should provide it as shown below. ````xml bin\AutoUpdaterTest.exe ```` -* args (Optional): You can provide command line arguments for Installer between this tag. You can include %path% with your command line arguments, it will be replaced by path of the directory where currently executing application resides. -* checksum (Optional): You can provide the checksum for the update file between this tag. If you do this AutoUpdater.NET will compare the checksum of the downloaded file before executing the update process to check the integrity of the file. You can provide algorithm attribute in the checksum tag to specify which algorithm should be used to generate the checksum of the downloaded file. Currently, MD5, SHA1, SHA256, SHA384, and SHA512 are supported. +* args (Optional): You can provide command line arguments for Installer between this tag. You can include %path% with + your command line arguments, it will be replaced by path of the directory where currently executing application + resides. +* checksum (Optional): You can provide the checksum for the update file between this tag. If you do this AutoUpdater.NET + will compare the checksum of the downloaded file before executing the update process to check the integrity of the + file. You can provide algorithm attribute in the checksum tag to specify which algorithm should be used to generate + the checksum of the downloaded file. Currently, MD5, SHA1, SHA256, SHA384, and SHA512 are supported. ````xml Update file Checksum ```` -You can also use the XML creator tool created by one of the user to create the XML file. You can download it from [here](https://github.com/DwainSnickles/AutoUpdater.NET.XML-Creator-master/blob/master/AutoUpdaterXML.zip). +You can also use the XML creator tool created by one of the user to create the XML file. You can download it +from [here](https://github.com/DwainSnickles/AutoUpdater.NET.XML-Creator-master/blob/master/AutoUpdaterXML.zip). ### Adding one line to make it work -After you done creating and uploading XML file, It is very easy to add a auto update functionality to your application. First you need to add following line at the top of your form. +After you done creating and uploading XML file, It is very easy to add a auto update functionality to your application. +First you need to add following line at the top of your form. ````csharp using AutoUpdaterDotNET; ```` -Now you just need to add following line to your main form constructor or in Form_Load event. You can add this line anywhere you like. If you don't like to check for update when application starts then you can create a Check for update button and add this line to Button_Click event. +Now you just need to add following line to your main form constructor or in Form_Load event. You can add this line +anywhere you like. If you don't like to check for update when application starts then you can create a Check for update +button and add this line to Button_Click event. ````csharp AutoUpdater.Start("https://rbsoft.org/updates/AutoUpdaterTest.xml"); @@ -91,7 +119,8 @@ Start method of AutoUpdater class takes URL of the XML file you uploaded to serv ### Current version detection -AutoUpdater.NET uses Assembly version to determine the current version of the application. You can update it by going to Properties of the project as shown in following screenshot. +AutoUpdater.NET uses Assembly version to determine the current version of the application. You can update it by going to +Properties of the project as shown in following screenshot. ![How to change assembly version of your .NET application?](https://rbsoft.org/images/assembly-version.png) @@ -107,7 +136,8 @@ AutoUpdater.Start("https://rbsoft.org/updates/AutoUpdaterTest.xml", myAssembly); ### Provide installed version manually -If you don't want AutoUpdater.NET to determine the installed version from assembly then you can provide your own version by assigning it to InstalledVersion field as shown below. +If you don't want AutoUpdater.NET to determine the installed version from assembly then you can provide your own version +by assigning it to InstalledVersion field as shown below. ````csharp AutoUpdater.InstalledVersion = new Version("1.2"); @@ -115,13 +145,15 @@ AutoUpdater.InstalledVersion = new Version("1.2"); ### Download Update file and XML using FTP -If you like to use ftp XML URL to check for updates or download the update file then you can provide you FTP credentials in alternative Start method as shown below. +If you like to use ftp XML URL to check for updates or download the update file then you can provide you FTP credentials +in alternative Start method as shown below. ````csharp AutoUpdater.Start("ftp://rbsoft.org/updates/AutoUpdaterTest.xml", new NetworkCredential("FtpUserName", "FtpPassword")); ```` -If you are using FTP download URL in the XML file then credentials provided here will be used to authenticate the request. +If you are using FTP download URL in the XML file then credentials provided here will be used to authenticate the +request. ### Check for updates synchronously @@ -149,7 +181,9 @@ AutoUpdater.ShowRemindLaterButton = false; ### Ignore previous Remind Later or Skip settings -If you want to ignore previously set Remind Later and Skip settings then you can set Mandatory property to true. It will also hide Skip and Remind Later button. If you set Mandatory to true in code then value of Mandatory in your XML file will be ignored. +If you want to ignore previously set Remind Later and Skip settings then you can set Mandatory property to true. It will +also hide Skip and Remind Later button. If you set Mandatory to true in code then value of Mandatory in your XML file +will be ignored. ````csharp AutoUpdater.Mandatory = true; @@ -157,7 +191,10 @@ AutoUpdater.Mandatory = true; ### Forced updates -You can enable forced updates by setting Mandatory property to true and setting UpdateMode to value of `Mode.Forced` or `Mode.ForcedDownload`. `Mode.Forced` option will hide Remind Later, Skip and Close buttons on the standard update dialog. `Mode.ForcedDownload` option will skip the standard update dialog and start downloading and updating the application without user interaction. `Mode.ForceDownload` option will also ignore value of OpenDownloadPage flag. +You can enable forced updates by setting Mandatory property to true and setting UpdateMode to value of `Mode.Forced` +or `Mode.ForcedDownload`. `Mode.Forced` option will hide Remind Later, Skip and Close buttons on the standard update +dialog. `Mode.ForcedDownload` option will skip the standard update dialog and start downloading and updating the +application without user interaction. `Mode.ForceDownload` option will also ignore value of OpenDownloadPage flag. ````csharp AutoUpdater.Mandatory = true; @@ -175,7 +212,8 @@ AutoUpdater.BasicAuthXML = AutoUpdater.BasicAuthDownload = AutoUpdater.BasicAuth ### Set User-Agent for http web requests -Set the User-Agent string to be used for HTTP web requests so you can differentiate them in your web server request logs. +Set the User-Agent string to be used for HTTP web requests so you can differentiate them in your web server request +logs. ````csharp AutoUpdater.HttpUserAgent = "AutoUpdater"; @@ -183,7 +221,8 @@ AutoUpdater.HttpUserAgent = "AutoUpdater"; ### Enable Error Reporting -You can turn on error reporting by adding below code. If you do this AutoUpdater.NET will show error message, if there is no update available or if it can't get to the XML file from web server. +You can turn on error reporting by adding below code. If you do this AutoUpdater.NET will show error message, if there +is no update available or if it can't get to the XML file from web server. ````csharp AutoUpdater.ReportErrors = true; @@ -191,7 +230,8 @@ AutoUpdater.ReportErrors = true; ### Run update process without Administrator privileges -If your application doesn't need administrator privileges to replace old version then you can set RunUpdateAsAdmin to false. +If your application doesn't need administrator privileges to replace old version then you can set RunUpdateAsAdmin to +false. ````csharp AutoUpdater.RunUpdateAsAdmin = false; @@ -199,17 +239,20 @@ AutoUpdater.RunUpdateAsAdmin = false; ### Open Download Page -If you don't want to download the latest version of the application and just want to open the URL between url tags of your XML file then you need to add following line with above code. +If you don't want to download the latest version of the application and just want to open the URL between url tags of +your XML file then you need to add following line with above code. ````csharp AutoUpdater.OpenDownloadPage = true; ```` -This kind of scenario is useful if you want to show some information to users before they download the latest version of an application. +This kind of scenario is useful if you want to show some information to users before they download the latest version of +an application. ### Remind Later -If you don't want users to select Remind Later time when they press the Remind Later button of update dialog then you need to add following lines with above code. +If you don't want users to select Remind Later time when they press the Remind Later button of update dialog then you +need to add following lines with above code. ````csharp AutoUpdater.LetUserSelectRemindLater = false; @@ -221,7 +264,9 @@ In above example when user press Remind Later button of update dialog, It will r ### Proxy Server -If your XML and Update file can only be used from certain Proxy Server then you can use following settings to tell AutoUpdater.NET to use that proxy. Currently, if your Changelog URL is also restricted to Proxy server then you should omit changelog tag from XML file cause it is not supported using Proxy Server. +If your XML and Update file can only be used from certain Proxy Server then you can use following settings to tell +AutoUpdater.NET to use that proxy. Currently, if your Changelog URL is also restricted to Proxy server then you should +omit changelog tag from XML file cause it is not supported using Proxy Server. ````csharp var proxy = new WebProxy("ProxyIP:ProxyPort", true) @@ -233,7 +278,8 @@ AutoUpdater.Proxy = proxy; ### Specify where to download the update file -You can specify where you want to download the update file by assigning DownloadPath field as shown below. It will be used for ZipExtractor too. +You can specify where you want to download the update file by assigning DownloadPath field as shown below. It will be +used for ZipExtractor too. ````csharp AutoUpdater.DownloadPath = Application.StartupPath; @@ -241,7 +287,8 @@ AutoUpdater.DownloadPath = Application.StartupPath; ### Specify where to extract zip file containing updated files -If you are using a zip file as an update file then you can set the "InstallationPath" equal to the path where your app is installed. This is only necessary when your installation directory differs from your executable path. +If you are using a zip file as an update file then you can set the "InstallationPath" equal to the path where your app +is installed. This is only necessary when your installation directory differs from your executable path. ````csharp var currentDirectory = new DirectoryInfo(Application.StartupPath); @@ -251,9 +298,20 @@ if (currentDirectory.Parent != null) } ```` +### Specify relative path to executable you want to execute after update + +If you are using a zip file as an update file, then you can set "ExecutablePath" equal to a new executable path relative to +the installation directory. This is only necessary if your new executable path differs from current executable path. +The "executable" value defined in XML takes precedence over this value. + +````csharp +AutoUpdater.ExecutablePath = "bin/AutoUpdater.exe"; +```` + ### Clear application directory before extracting update file -Sometimes it is necessary to clear previous version files before doing an update. In this case, you can specify whether to clear the application directory before extracting the update file using the below code. +Sometimes it is necessary to clear previous version files before doing an update. In this case, you can specify whether +to clear the application directory before extracting the update file using the below code. ````csharp AutoUpdater.ClearAppDirectory = true; @@ -269,16 +327,20 @@ AutoUpdater.UpdateFormSize = new System.Drawing.Size(800, 600); ### Change storage method of Remind Later and Skip options -You can change how AutoUpdater.NET saves the Remind Later and Skip values by assigning the PersistenceProvider. If you don't provide a PersistenceProvider then it will save the values in Windows registry. +You can change how AutoUpdater.NET saves the Remind Later and Skip values by assigning the PersistenceProvider. If you +don't provide a PersistenceProvider then it will save the values in Windows registry. -If you are using .NET 4.0 or above then you can use JsonFilePersistenceProvider instead of default RegistryPersistenceProvider as shown below. +If you are using .NET 4.0 or above then you can use JsonFilePersistenceProvider instead of default +RegistryPersistenceProvider as shown below. ````csharp string jsonPath = Path.Combine(Environment.CurrentDirectory, "settings.json"); AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath); ```` -You can create your own PersistenceProvider by implementing [IPersistenceProvider](https://github.com/ravibpatel/AutoUpdater.NET/blob/master/AutoUpdater.NET/IPersistenceProvider.cs) interface. +You can create your own PersistenceProvider by +implementing [IPersistenceProvider](https://github.com/ravibpatel/AutoUpdater.NET/blob/master/AutoUpdater.NET/IPersistenceProvider.cs) +interface. ## Check updates frequently @@ -312,7 +374,8 @@ timer.Start(); ## Handling Application exit logic manually -If you like to handle Application exit logic yourself then you can use ApplicationExitEvent like below. This is very useful if you like to do something before closing the application. +If you like to handle Application exit logic yourself then you can use ApplicationExitEvent like below. This is very +useful if you like to do something before closing the application. ````csharp AutoUpdater.ApplicationExitEvent += AutoUpdater_ApplicationExitEvent; @@ -327,7 +390,9 @@ private void AutoUpdater_ApplicationExitEvent() ## Handling updates manually -Sometimes as a developer you need to maintain look and feel for the entire application similarly or you just need to do something before update. In this type of scenarios you can handle the updates manually by subscribing to an event. You can do it by adding following line with above code. +Sometimes as a developer you need to maintain look and feel for the entire application similarly or you just need to do +something before update. In this type of scenarios you can handle the updates manually by subscribing to an event. You +can do it by adding following line with above code. ````csharp AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent; @@ -401,7 +466,8 @@ private void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args) } ```` -When you do this it will execute the code in above event when AutoUpdater.Start method is called instead of showing the update dialog. +When you do this it will execute the code in above event when AutoUpdater.Start method is called instead of showing the +update dialog. * IsUpdateAvailable (bool) : If update is available then returns true otherwise false. * DownloadURL (string) : Download URL of the update file.. @@ -412,7 +478,8 @@ When you do this it will execute the code in above event when AutoUpdater.Start ## Handling parsing logic manually -If you want to use other format instead of XML as an AppCast file then you need to handle the parsing logic by subscribing to ParseUpdateInfoEvent. You can do it as follows. +If you want to use other format instead of XML as an AppCast file then you need to handle the parsing logic by +subscribing to ParseUpdateInfoEvent. You can do it as follows. ````csharp AutoUpdater.ParseUpdateInfoEvent += AutoUpdaterOnParseUpdateInfoEvent; @@ -464,7 +531,7 @@ private void AutoUpdaterOnParseUpdateInfoEvent(ParseUpdateInfoEventArgs args) You can follow below steps to build the project on your local development environment. -* Disable signing from project properties of both AutoUpdater.NET and ZipExtractor. +* Disable signing from project properties of both AutoUpdater.NET and ZipExtractor. * Edit both .csproj file of AutoUpdater.NET and ZipExtractor and change following line. Use .NET version you prefer. Before @@ -480,4 +547,6 @@ You can follow below steps to build the project on your local development enviro ``` * Build ZipExtractor project in "Release" configuration to create the executable in Resources folder. -* VS2022 doesn't allow building .NET Framework 4.5 by default, so if you are using it then you can just change it to any supported .NET version, or you have to follow steps from [here](https://stackoverflow.com/a/70109092/1273550) to use .NET Framework 4.5. +* VS2022 doesn't allow building .NET Framework 4.5 by default, so if you are using it then you can just change it to any + supported .NET version, or you have to follow steps from [here](https://stackoverflow.com/a/70109092/1273550) to use + .NET Framework 4.5. diff --git a/ZipExtractor/FormMain.cs b/ZipExtractor/FormMain.cs index 0c7a5d7..86332e2 100644 --- a/ZipExtractor/FormMain.cs +++ b/ZipExtractor/FormMain.cs @@ -187,50 +187,47 @@ private void FormMain_Shown(object sender, EventArgs e) const int errorSharingViolation = 0x20; const int errorLockViolation = 0x21; var errorCode = Marshal.GetHRForException(exception) & 0x0000FFFF; - if (errorCode is errorSharingViolation or errorLockViolation) + if (errorCode is not (errorSharingViolation or errorLockViolation)) { - retries++; - if (retries > MaxRetries) - { - throw; - } + throw; + } - List lockingProcesses = null; - if (Environment.OSVersion.Version.Major >= 6 && retries >= 2) - { - try - { - lockingProcesses = FileUtil.WhoIsLocking(filePath); - } - catch (Exception) - { - // ignored - } - } + retries++; + if (retries > MaxRetries) + { + throw; + } - if (lockingProcesses == null) + List lockingProcesses = null; + if (Environment.OSVersion.Version.Major >= 6 && retries >= 2) + { + try { - Thread.Sleep(5000); + lockingProcesses = FileUtil.WhoIsLocking(filePath); } - else + catch (Exception) { - foreach (var lockingProcess in lockingProcesses) - { - var dialogResult = MessageBox.Show( - string.Format(Resources.FileStillInUseMessage, - lockingProcess.ProcessName, filePath), - Resources.FileStillInUseCaption, - MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); - if (dialogResult == DialogResult.Cancel) - { - throw; - } - } + // ignored } } - else + + if (lockingProcesses == null) { - throw; + Thread.Sleep(5000); + continue; + } + + foreach (var lockingProcess in lockingProcesses) + { + var dialogResult = MessageBox.Show( + string.Format(Resources.FileStillInUseMessage, + lockingProcess.ProcessName, filePath), + Resources.FileStillInUseCaption, + MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); + if (dialogResult == DialogResult.Cancel) + { + throw; + } } } } @@ -267,28 +264,27 @@ private void FormMain_Shown(object sender, EventArgs e) throw eventArgs.Error; } - if (!eventArgs.Cancelled) + if (eventArgs.Cancelled) return; + + textBoxInformation.Text = @"Finished"; + try { - textBoxInformation.Text = @"Finished"; - try + var executablePath = string.IsNullOrWhiteSpace(updatedExe) ? currentExe : Path.Combine(extractionPath, updatedExe); + ProcessStartInfo processStartInfo = new ProcessStartInfo(executablePath); + if (!string.IsNullOrEmpty(commandLineArgs)) { - var executablePath = updatedExe != null ? Path.Combine(extractionPath, updatedExe) : currentExe; - ProcessStartInfo processStartInfo = new ProcessStartInfo(executablePath); - if (!string.IsNullOrEmpty(commandLineArgs)) - { - processStartInfo.Arguments = commandLineArgs; - } + processStartInfo.Arguments = commandLineArgs; + } - Process.Start(processStartInfo); + Process.Start(processStartInfo); - _logBuilder.AppendLine("Successfully launched the updated application."); - } - catch (Win32Exception exception) + _logBuilder.AppendLine("Successfully launched the updated application."); + } + catch (Win32Exception exception) + { + if (exception.NativeErrorCode != 1223) { - if (exception.NativeErrorCode != 1223) - { - throw; - } + throw; } } }