π GeneralUpdate Integration Guide
Helps developers integrate GeneralUpdate auto-update into any .NET application. Start from scratch β no prior knowledge of GeneralUpdate internals required.
First: Why do you need two programs?β
GeneralUpdate uses a dual-process architecture. Understanding this is key to everything else:
Your App.exe (Client β main application)
β
βββ Normally: runs your business logic
β
βββ When an update is found:
β 1. Downloads the update package
β 2. Writes download info to an IPC file (temporary file)
β 3. Launches the "upgrade program" (UpgradeApp.exe)
β 4. Exits itself (Windows won't let a program overwrite itself)
β
βΌ
UpgradeApp.exe (Upgrade β update installer)
β
βββ Reads the IPC file
βββ Extracts/replaces files (main program has exited, files are free)
βββ Launches the main program
βββ Exits itself
IPC file (Inter-Process Communication) = a temporary file. The Client writes update info into it; the Upgrade reads it to perform the actual update.
manifest.json = a manifest file that tells GeneralUpdate what your main program is called and where the upgrade program lives.
The reason is simple: Windows doesn't allow a running program to overwrite its own .exe, so a separate "helper" process is needed to do the file replacement.
What you'll needβ
1. Your .NET project (WPF / WinForms / Avalonia / MAUI / Console β any works)
2. .NET SDK (8.0+)
3. A backend service (optional β you can use OSS object storage instead)
Install NuGetβ
dotnet add package GeneralUpdate.Core --version 10.5.0-beta.7
If you need the crash daemon (Bowl), add one more package:
dotnet add package GeneralUpdate.Bowl --version 10.5.0-beta.7
Differential update is built into Core β no need for a separateGeneralUpdate.Differentialpackage.
Minimum setup: 3 lines to get it runningβ
This is the simplest integration, suitable for console apps or just verifying functionality:
using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;
var config = new UpdateRequest
{
UpdateUrl = "https://your-server.com/api", // Server API endpoint
AppSecretKey = "your-secret-key", // Secret key (β₯ 16 chars)
MainAppName = "MyApp.exe", // Your main program filename
ClientVersion = "1.0.0.0", // Current version
ProductId = "my-product-001", // Your product ID
InstallPath = "." // Install directory (. = current dir)
};
await new GeneralUpdateBootstrap().SetConfig(config).LaunchAsync();
These 6 fields are required β missing any will cause an error:
| Field | What to put | Why it's needed |
|---|---|---|
UpdateUrl | Server API URL | The program queries this for new versions |
AppSecretKey | Your chosen key | Used for IPC encrypted communication; must match between Client & Upgrade |
MainAppName | "MyApp.exe" | Tells the Upgrade which exe to launch after updating |
ClientVersion | "1.0.0.0" | Server uses this to decide which versions to send |
ProductId | "my-app-001" | Differentiates products (if you have multiple apps) |
InstallPath | "." | Where to extract update packages |
Full setup: with event listenersβ
If you want to see download progress, handle errors, or add logging, attach event listeners:
using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;
using GeneralUpdate.Core.Download;
var config = new UpdateRequest
{
UpdateUrl = "https://your-server.com/Upgrade/Verification",
AppSecretKey = "your-secret-key",
MainAppName = "MyApp.exe",
ClientVersion = "1.0.0.0",
ProductId = "my-product-001",
InstallPath = AppDomain.CurrentDomain.BaseDirectory, // Use this in production
};
await new GeneralUpdateBootstrap()
.SetConfig(config)
.AddListenerUpdateInfo((_, e) =>
{
// Fires when update info is available
Console.WriteLine($"Found {e.Info?.Body?.Count ?? 0} versions");
})
.AddListenerMultiDownloadStatistics((_, e) =>
{
// Download progress (bind to your progress bar)
Console.WriteLine($"Progress: {e.ProgressPercentage}% | Speed: {e.Speed}");
})
.AddListenerMultiDownloadCompleted((_, e) =>
{
Console.WriteLine($"Version {e.Version} downloaded");
})
.AddListenerMultiDownloadError((_, e) =>
{
Console.WriteLine($"Download failed: {e.Exception?.Message}");
})
.AddListenerMultiAllDownloadCompleted((_, e) =>
{
Console.WriteLine("All downloads complete, starting update");
})
.AddListenerException((_, e) =>
{
Console.WriteLine($"Error: {e.Message}");
})
.AddListenerProgress((_, e) =>
{
// v10.5.0-beta.7 new β the 7th event
})
.LaunchAsync();
You usually only need 3 events:
MultiDownloadStatistics(progress bar),MultiDownloadError(download failed),Exception(errors). Add others as needed.
UpdateRequest full referenceβ
Beyond the 6 required fields, these optional settings are available:
var config = new UpdateRequest
{
// === Required ===
UpdateUrl = "...", AppSecretKey = "...", MainAppName = "...",
ClientVersion = "1.0.0.0", ProductId = "...", InstallPath = ".",
// === Optional: Authentication ===
AuthScheme = AuthScheme.Hmac, // Hmac / Bearer / ApiKey / Basic
Token = "your-token",
BasicUsername = "user",
BasicPassword = "pass",
// === Optional: Files to exclude during backup ===
Files = new List<string> { "*.log", "*.tmp" },
Formats = new List<string> { ".pdb" },
Directories = new List<string> { "logs", "cache" },
};
You can also use the builder pattern (same result, more fluent syntax):
var config = UpdateRequestBuilder.Create()
.SetUpdateUrl("https://your-server.com/api")
.SetAppSecretKey("your-secret-key")
.SetMainAppName("MyApp.exe")
.SetClientVersion("1.0.0.0")
.SetProductId("my-product-001")
.SetInstallPath(".")
.Build();
Or use zero-config mode (auto-discovers from manifest.json):
await new GeneralUpdateBootstrap()
.SetSource(
updateUrl: "https://your-server.com/api",
appSecretKey: "your-secret-key")
.AddListenerUpdateInfo(...)
.LaunchAsync();
4 update scenarios (based on server response)β
GeneralUpdate auto-selects its action based on what the server returns:
| Scenario | What happens |
|---|---|
| None | No update β launch main program |
| UpgradeOnly | Update upgrade app only β extract and overwrite |
| MainOnly | Update main app only β download β IPC β Upgrade replaces files |
| Both | Update both |
Upgrade process configurationβ
The Upgrade (installer) doesn't need configuration β it reads from the IPC file automatically:
using GeneralUpdate.Core;
await new GeneralUpdateBootstrap()
.AddListenerException((_, e) =>
Console.WriteLine($"Error: {e.Message}"))
.LaunchAsync();
Your project needs two separate projects: one Client (main app) and one Upgrade (installer).
Deployment checklistβ
Your publish directory should look like:
publish/
βββ MyApp.exe β Your main program
βββ generalupdate.manifest.json β Update manifest
βββ update/
βββ UpgradeApp.exe β Upgrade program (must exist from v1.0!)
The most common first-time mistake is forgetting to include UpgradeApp.exe, which makes updates impossible to execute.
App type referenceβ
AppType distinguishes the two processes:
| Value | Enum | Who uses it |
|---|---|---|
| 1 | AppType.Client | Main program (your business code) |
| 2 | AppType.Upgrade | Upgrade program (UpgradeApp.exe) |
| 3 | AppType.OssClient | OSS main program |
| 4 | AppType.OssUpgrade | OSS upgrade program |
Common beginner mistakesβ
| # | Mistake | Consequence | Correct approach |
|---|---|---|---|
| 1 | Client and Upgrade use different NuGet versions | Method not found at runtime | Both projects use the exact same version |
| 2 | Forgot to include UpgradeApp.exe | Update detected but can't execute | Include it in update/ from the first release |
| 3 | IPC encoding not set to UTF-8 | Garbled text on Linux/macOS | Set Encoding.UTF8 |
| 4 | Version not in 4-part format (e.g. 1.0) | Version comparison breaks | Always use 1.0.0.0 format |
| 5 | manifest.json exe name doesn't match | Can't find main program after update | Match the actual filename exactly |
Next stepsβ
- Need an update UI β generalupdate-ui
- Choosing an update strategy β generalupdate-strategy
- Need crash daemon / advanced features β generalupdate-advanced
- Running into issues β generalupdate-troubleshoot