.NET Client Program Auto-Update
When we develop client programs that need to be deployed on multiple hosts in daily development, upgrading them one by one can be very troublesome if the program needs to be updated. At this point, the .NET client program auto-update technology described in this article can be used.
The auto-update technology described in this article mainly uses the open-source GeneralUpdate component, which can be used for auto-updating applications such as Winform/WPF/ConsoleApp.
The GeneralUpdate component is developed and maintained by a Microsoft MVP. The repository address and screenshots are as follows. The usage documentation and videos provided by the author are somewhat too simple, and there are compatibility issues between different versions, which are not well explained. Therefore, developers who first encounter this component may be a bit confused. The author combines his actual usage in projects to introduce the usage of this component in more detail.
GitHub: https://github.com/WELL-E/AutoUpdater

Gitee: https://gitee.com/Juster-zhu/GeneralUpdate

Auto-Update Flowchart

Given that the original diagram’s explanation is not clear enough, the author added explanations in red font in the above diagram. The diagram appears to show the interaction of three components or services, but it actually involves four:
- Client program version verification service (optional): This service provides at least two APIs—one to determine whether the client program has the latest version, and another to retrieve all updated versions of the current client. Sometimes we don’t want to write and deploy a separate verification service, so we can directly use a database as a substitute. The client program directly queries the database to determine and retrieve all updated versions of the current program.
- Client program (required): The business program that needs auto-update functionality can obtain its own assembly version via reflection and compare it with the server/database to determine if there is a new version.
- Update component (required): The update component is actually a separate executable file placed in the same directory as the client program. Its main function is to download all update compressed packages of the client program from a specified path and extract them one by one, achieving version-by-version upgrades of the client program. When the client obtains the path of the files to be updated from the server, it needs to start the update component via inter-process communication. After the update component starts, it must close the client program to prevent some files from being occupied, which could cause update failure. After the update component successfully completes the update, it restarts the client and closes itself, completing the auto-update.
- File server (required): The update compressed packages of the client program are uploaded to the file server to obtain the URL for each package. The update component downloads the program based on this URL. The file server the author used is HFS, with the download link: HFS Download.
Code Structure Analysis

In the above diagram, the projects starting with GeneralUpdate are the core code for the auto-update functionality. The packages for each project can be seen on the NuGet server. Which specific package to use depends on whether you want to implement self-updating of the update component, updating of the client program, or writing a version verification service. Refer to the introduction in the framework’s README.md.
It is important to note that the above components are not backward compatible! Many methods in the 3.x.x version of the component have been renamed, so you cannot directly upgrade from version 2.x.x.
In the above diagram, the projects starting with AutoUpdate are simple implementations of the three main components in the auto-update flowchart:
ConsoleApp: Console version DEMO of the update component (needs to work with a file server, references GeneralUpdate.Core)MauiApp-Sample: Not studied in detail, unclearMinimalService: Client version verification service DEMO (references GeneralUpdate.AspNetCore)Test: WPF version DEMO for self-updating of the update component (needs to work with MinimalService, references GeneralUpdate.ClientCore)WpfApp: DEMO for using theGeneralUpdate.Singlepackage, used to build a singleton version of the update component (references GeneralUpdate.Single)WpfNet6-Sample: WPF version program for updating the update component.
Practical Auto-Update for Winform Applications
From the description in the previous section, if we don’t want to write a client version verification service and only want to update the client program via a file server, then we only need a console version of the update component. Therefore, you can refer to the code in the ConsoleApp project.
Console Implementation of the Update Component
Note: This example uses version 2.1.6 of GeneralUpdate.Core. Because the source code on GitHub has been upgraded to version 3.x.x, supporting .NET 6.0, but the author’s computer lacks the relevant framework and cannot compile it. Therefore, the source code was checked out to a specific commit, so if problems arise during use, the issue can be resolved by debugging the source code. If you fully understand the meaning of this article, you can directly install the latest version of the NuGet package and refer to the relevant examples in the latest source code.
using System;
using System.ComponentModel;
using System.Diagnostics;
using GeneralUpdate.Core;
using GeneralUpdate.Core.Strategy;
using GeneralUpdate.Core.Update;
using ProgressChangedEventArgs = GeneralUpdate.Core.Update.ProgressChangedEventArgs;
namespace AutoUpdate.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
// args = new []{
// "1.0.1",
// "1.0.2",
// "",
// "http://127.0.0.1:7000/client_v1.0.2.zip",
// @"D:\Project",
// "36aad55a19f85ee6e1fbdc26510a26c1"
// };
KillProcess("Your Client Program Name, without exe");
GeneralUpdateBootstrap bootstrap = new GeneralUpdateBootstrap();
bootstrap.DownloadStatistics += OnDownloadStatistics;
bootstrap.ProgressChanged += OnProgressChanged;
bootstrap.Strategy<DefultStrategy>().
Option(UpdateOption.Format, "zip").
Option(UpdateOption.MainApp, "Your Client Program Name, without exe").
Option(UpdateOption.DownloadTimeOut, 60).
RemoteAddress(args).
Launch();
Console.ReadKey();
}
private static void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.Type == ProgressType.Updatefile)
{
var str = $"Currently updating the {e.ProgressValue}th file, total update files: {e.TotalSize}";
Console.WriteLine(str);
}
if (e.Type == ProgressType.Done)
{
Console.WriteLine("Update completed");
}
if (e.Type == ProgressType.Fail)
{
Console.WriteLine(e.Message);
}
}
private static void OnDownloadStatistics(object sender, DownloadStatisticsEventArgs e)
{
Console.WriteLine($"Download speed: {e.Speed}, remaining time: {e.Remaining.Minute}:{e.Remaining.Second}");
}
private static void KillProcess(string processName)
{
foreach (var process in Process.GetProcesses())
{
if (!process.ProcessName.ToUpper().Contains(processName.ToUpper())) continue;
try
{
process.Kill();
process.WaitForExit();
}
catch (Win32Exception)
{
}
}
}
}
}
Client Invocation
Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
var ver = $"{version.Major}.{version.Minor}.{version.Build}";
// Retrieve version information higher than the current assembly version from the database
var versionInfo = TOSBll.Instance.GetLastUpdateVersionInfo(1, ver);
if (versionInfo != null)
{
string para =
$"{ver} {versionInfo.VERSION} \"\" {versionInfo.URL} {Environment.CurrentDirectory} {versionInfo.MD5}";
ExecuteAsAdmin("AutoUpdate.ConsoleApp.exe", para);
return;
}
private static void ExecuteAsAdmin(string fileName, string args)
{
Process proc = new Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";
proc.StartInfo.Arguments = args;
proc.Start();
}
From the above code, it can be seen that the client uses inter-process communication to start the update component and pass in the update parameter information. Here, the update component is started with administrator privileges to avoid update failure (the component needs to copy files to the system’s temporary directory during the update, delete them after success, and insufficient permissions may cause errors). However, the author found during testing that this method of startup still fails. It was only successful by right-clicking the AutoUpdate.ConsoleApp.exe program and attaching administrator privileges.
Several Criticisms
- Key versions are not tagged, making it impossible for users to switch to NuGet package version 2.1.6 by checking out a specific commit.
- New versions of the component are not backward compatible with older versions.
- The code used in the unit test file is from the old version, while the component source code is from the new version, which directly confuses those who first encounter the component.
- There are still some small bugs, such as
FileUtil.Update32Or64Libs()throwing an exception because a directory is deleted twice, causing the first update to fail on the initial launch, but succeeding on the second update because it has already been deleted. The author has submitted an Issue, but it is unclear when the author will fix it. - The documentation is too simple.
Summary
Although the GeneralUpdate component has some shortcomings, I believe that after this introduction, everyone knows how to avoid pitfalls when using this component. Overall, the component’s functionality is quite easy to use. Considering that the component is maintained by only one person, it is already quite good. I would like to thank the author for their contribution.