Skip to main content

GeneralUpdate.Core

Namespace: GeneralUpdate.Core | Main Entry Point: GeneralUpdateBootstrap | NuGet Package: GeneralUpdate.Core

1. Component Overview

1.1 Introduction

GeneralUpdate.Core is the update execution engine of the GeneralUpdate ecosystem, responsible for full lifecycle update management of client applications. It provides a programmable launcher, configuration models, event notification system, download subsystem (supporting concurrency, resume, retry, verification, and post-processing pipelines), differential patch pipeline, version write-back, IPC process communication, and platform strategy extensions.

Core Capabilities:

CapabilityDescription
Multi-Strategy Update ExecutionBuilt-in standard Client/Upgrade, OSS object storage, and silent background polling strategies
Configuration-DrivenStrongly-typed UpdateRequest or lightweight SetSource entry, with generalupdate.manifest.json for minimal configuration
Download SubsystemPluggable download sources, executors, retry policies, post-processing pipelines, and orchestrators; defaults include concurrent downloads, resume, and SHA256 verification
Differential Patch PipelineFile-level binary diff (BSDIFF 4.0 / Streaming HDiff), directory-level comparison and batch patch distribution with parallel processing
Event Notifications7 event callbacks (version discovery, download progress, completion, error, exception, etc.) with batch listener registration
Extension Points10 pluggable interfaces: lifecycle hooks, status reporting, SSL certificate policy, HTTP authentication, download source/policy/executor/pipeline/orchestrator, platform strategy
Manifest Systemgeneralupdate.manifest.json for auto-discovery of app identity and automatic version write-back
IPC CommunicationEncrypted file-based context passing between main app and upgrade process
SignalR Real-Time PushVersion update push via SignalR (UpgradeHubService), supporting peer-to-peer and broadcast, auto-reconnect, multi-event subscription

Business Problems Solved:

  • Desktop apps need reliable auto-update, but hand-writing update logic involves version comparison, download, verification, extraction, file replacement, and process restart
  • Full updates for large apps have high bandwidth costs; differential updates reduce download size
  • Flexible update strategies needed (silent background, user-triggered, OSS/CDN distribution)
  • Upgrade process versioning needs coordination between main app and upgrade process

Use Cases:

  • Auto-update for WPF / WinForms / Avalonia / WinUI desktop applications
  • Unified version management for enterprise internal tools
  • Client apps distributing update packages via CDN / OSS
  • Large clients needing differential updates to reduce bandwidth
Core's Role in the Ecosystem

GeneralUpdate.Core is the core engine of the ecosystem. It doesn't generate patches (that's Tools/Differential's job) and doesn't recover from crashes (that's Bowl's job) — it orchestrates the entire update flow: from version checking, downloading, and verification, to launching the Upgrade process for file replacement.

1.2 Environment & Dependencies

ItemDescription
Version10.5.0-beta.7
Target Frameworknetstandard2.0 (.NET Framework 4.6.1+ / .NET Core 2.0+ / .NET 5+)
DependenciesGeneralUpdate.Differential, System.Text.Json, Microsoft.Extensions.Logging.Abstractions
CompatibilityWindows (primary) / Linux / macOS; x86 / x64 / ARM64

2. Feature List

FeatureDescriptionTypeRequiredNotes
Standard Client UpdateMain app checks version, downloads packages, launches upgrade processCoreRequiredRequires server version check API
Standard Upgrade UpdateStandalone upgrade process reads IPC context and executes file replacement, diff patches, version write-backCoreRequiredLaunched by main app via encrypted IPC
OSS Client UpdateDownload version config from OSS/CDN, compare, launch upgrade processCoreOptionalVersion config hosted on object storage
OSS Upgrade UpdateOSS-mode upgrade process downloads and extracts resource packagesCoreOptionalPaired with OssClient
Silent Background UpdateBackground polling, silent download, upgrade on process exitCoreOptionalSet Option.Silent = true
Differential Patch PipelineFile-level binary diff generation & application, directory-level batch distributionCoreOptionalRequires Option.PatchEnabled = true
Concurrent DownloadsMulti-asset concurrent download with resume & SHA256 verificationCoreOptionalControlled via Option.MaxConcurrency
Event Callbacks7 event types: version info, progress, completion, errors, exceptionsCoreOptionalRegistered via AddListener* methods
App Identity Manifestgeneralupdate.manifest.json auto-discovery & version write-backExtendedRecommendedGenerated by GeneralUpdate.Tools
Custom Download SourceCustom version list and download resource sourceExtendedOptionalImplement IDownloadSource
Custom Download ExecutorCustom single-file download (HTTP/FTP/SFTP etc.)ExtendedOptionalImplement IDownloadExecutor
Custom Retry PolicyCustom retry, timeout, circuit-breaking strategyExtendedOptionalImplement IDownloadPolicy
Custom Download PipelinePost-download processing (verification, decryption, scanning)ExtendedOptionalImplement IDownloadPipeline
Custom Download OrchestratorFully replace batch download concurrency controlExtendedOptionalImplement IDownloadOrchestrator
Lifecycle HooksBusiness logic injection: before/after update, download complete, error, before startExtendedOptionalImplement IUpdateHooks
Status ReportingReport update status to your own serverExtendedOptionalImplement IUpdateReporter
HTTP AuthenticationCustom HTTP request authentication headersExtendedOptionalImplement IHttpAuthProvider
SSL Certificate PolicyCustom HTTPS certificate validation logicExtendedOptionalImplement ISslValidationPolicy
Platform StrategyReplace platform-level file operations or launch logicExtendedOptionalImplement IStrategy
SignalR Real-Time PushServer proactively pushes version update notifications to connected clientsExtendedOptionalUpgradeHubService, namespace GeneralUpdate.Core.Hubs
Push Reconnect MechanismAuto-reconnect on disconnect (random backoff strategy), connection lifecycle managementExtendedOptionalRandomRetryPolicy
Push Event SubscriptionFour events: receive message, online status, reconnect notification, close notificationExtendedOptionalRegistered via AddListener* methods

3. API Configuration Reference

3.1 Configuration Properties (Props)

UpdateRequest Properties:

FieldTypeDefaultRequiredValuesDescription
UpdateUrlstringYesValid absolute URLUpdate check API endpoint
UpdateAppNamestring"Update.exe"RecommendedValid filenameUpgrade process filename
MainAppNamestringRecommendedValid filenameMain app filename for restart & identification
ClientVersionstringRecommendedSemVer formatCurrent main app version
AppSecretKeystringRecommendedApp key for server authentication
InstallPathstringBaseDirectoryOptionalValid directory pathApplication install root
ReportUrlstringnullOptionalValid absolute URLStatus report API
UpdateLogUrlstringnullOptionalValid absolute URLChangelog page URL
UpgradeClientVersionstringOptionalSemVer formatUpgrade process version
ProductIdstringOptionalProduct identifier for multi-product servers
UpdatePathstringInstallPathOptionalValid directory pathUpgrade process location
BowlstringnullOptionalValid filenameAuxiliary process name to close before update
SchemestringnullOptional"Bearer" etc.Auth scheme (used with Token)
TokenstringnullOptionalAuth token
AuthSchemeAuthSchemeHmacOptionalHmac, Bearer, BasicAuth scheme enum; Hmac for GeneralSpacestation server-side signed auth
BasicUsernamestringnullOptionalHTTP Basic auth username (requires AuthScheme = Basic)
BasicPasswordstringnullOptionalHTTP Basic auth password (requires AuthScheme = Basic)
FilesList<string>nullOptionalFiles to skip during update
FormatsList<string>nullOptionalExtensions to skip during update
DirectoriesList<string>nullOptionalDirectories to skip during update
DriverDirectorystringnullOptionalValid directory pathDriver update directory

Option Runtime Options:

FieldTypeDefaultRequiredValuesDescription
Option.AppTypeAppTypeClientYesClient(1), Upgrade(2), OssClient(3), OssUpgrade(4)Current process role
Option.DiffModeDiffModeSerialOptionalSerial, ParallelDownload execution mode
Option.EncodingEncodingUTF8OptionalEncoding instanceArchive processing encoding
Option.FormatFormatZipOptionalZipPackage format
Option.DownloadTimeoutint?30OptionalPositive integer (sec)Download timeout
Option.PatchEnabledbool?trueOptionaltrue / falseEnable differential patching
Option.BackupEnabledbool?trueOptionaltrue / falseBackup files before update
Option.SilentboolfalseOptionaltrue / falseEnable silent polling
Option.SilentPollIntervalMinutesint60OptionalPositive integerPolling interval (minutes)
Option.LaunchClientAfterUpdatebooltrueOptionaltrue / falseLaunch main app after upgrade
Option.MaxConcurrencyint3Optional1 ~ ProcessorCount × 2Max download concurrency
Option.EnableResumebooltrueOptionaltrue / falseEnable HTTP Range resume
Option.RetryCountint3OptionalNon-negative integerDownload retry count
Option.VerifyChecksumbooltrueOptionaltrue / falseVerify download file hash
Option.RetryIntervalTimeSpan1sOptionalPositive TimeSpanDownload retry interval

3.2 Instance Methods

GeneralUpdateBootstrap:

MethodParametersReturnsUse CaseNotes
LaunchAsync()NoneTask<GeneralUpdateBootstrap>Final entry for all Core scenariosAuto-selects strategy based on Option.AppType
Cancel()NonevoidUI "Cancel Update" buttonTriggers internal CancellationTokenSource
SetConfig(UpdateRequest)configInfoGeneralUpdateBootstrapExplicit update configurationCalls Validate() on key fields
SetConfig(string)filePath — JSON config file pathGeneralUpdateBootstrapRead config from fileSupports relative/absolute paths; UTF-8 JSON
SetSource(...)updateUrl, appSecretKey, reportUrl?, scheme?, token?, authScheme?, basicUsername?, basicPassword?, installPath?GeneralUpdateBootstrapLightweight entry with manifestIdentity info filled by manifest; supports HMAC / Bearer / Basic auth methods
SetOption(Option<T>, T)option — key, value — valueGeneralUpdateBootstrapSet runtime optionsPass null to reset nullable options
UseDiffPipeline(Action<DiffPipelineBuilder>)configure — delegateGeneralUpdateBootstrapReplace or tune diff pipelineDefault used if not called
AddListenerUpdateInfo(...)EventHandler<UpdateInfoEventArgs>GeneralUpdateBootstrapReceive server version infoAlso fires when no update available
AddListenerUpdatePrecheck(...)Func<UpdateInfoEventArgs, bool>GeneralUpdateBootstrapPre-download checkReturn true to skip non-forced update
AddListenerProgress(...)EventHandler<ProgressEventArgs>GeneralUpdateBootstrapProgress bar, status textContains both download & diff progress
AddListenerMultiDownloadCompleted(...)EventHandler<MultiDownloadCompletedEventArgs>GeneralUpdateBootstrapMark single asset download completionNot "all downloads complete"
AddListenerMultiAllDownloadCompleted(...)EventHandler<MultiAllDownloadCompletedEventArgs>GeneralUpdateBootstrapPost-all-downloads processingIncludes FailedVersions summary
AddListenerMultiDownloadError(...)EventHandler<MultiDownloadErrorEventArgs>GeneralUpdateBootstrapLog single download failureOverall success still determined by MultiAllDownloadCompleted
AddListenerMultiDownloadStatistics(...)EventHandler<MultiDownloadStatisticsEventArgs>GeneralUpdateBootstrapDisplay speed & ETAPrefer AddListenerProgress for new code
AddListenerException(...)EventHandler<ExceptionEventArgs>GeneralUpdateBootstrapReport exceptions, show errorsNotification only; no automatic retry
AddEventListener<TListener>()Generic — listener typeGeneralUpdateBootstrapBatch register event listenersT must implement IUpdateEventListener
Hooks<T>()Generic — hook typeGeneralUpdateBootstrapRegister lifecycle hooksT needs parameterless constructor
UpdateReporter<T>()Generic — reporter typeGeneralUpdateBootstrapRegister status reporter
SslPolicy<T>()Generic — SSL policy typeGeneralUpdateBootstrapCustom HTTPS certificate validationDon't unconditionally return true in production
HttpAuth<T>()Generic — auth provider typeGeneralUpdateBootstrapCustom HTTP auth
DownloadSource<T>()Generic — download source typeGeneralUpdateBootstrapCustom version list source
DownloadPolicy<T>()Generic — download policy typeGeneralUpdateBootstrapCustom retry/timeout policy
DownloadExecutor<T>()Generic — executor typeGeneralUpdateBootstrapCustom single-file download
DownloadPipeline<T>()Generic — pipeline typeGeneralUpdateBootstrapCustom post-download processing
DownloadOrchestrator<T>()Generic — orchestrator typeGeneralUpdateBootstrapFully replace batch downloadOnly when complete replacement needed
Strategy<T>()Generic — strategy typeGeneralUpdateBootstrapCustom platform strategy

UpgradeHubService:

MethodParametersReturnsUse CaseNotes
UpgradeHubService(string, string?, string?)url — SignalR Hub URL; token — optional ID4 auth token; appkey — optional client unique ID— (constructor)Create push service instanceappkey used for server-side targeted push; recommended to use a fixed GUID
StartAsync()NoneTaskEstablish SignalR long-lived connectionCan re-call after StopAsync
StopAsync()NoneTaskGracefully stop connection, retain reconnect abilitySuitable when app goes to background
DisposeAsync()NoneTaskFully release Hub and all resourcesCannot be reused after disposal
AddListenerReceive(Action<string>)receiveMessageCallbackvoidSubscribe to server push messagesMessage content is JSON string from server
AddListenerOnline(Action<string>)onlineMessageCallbackvoidSubscribe to online/offline status changes
AddListenerReconnected(Func<string?, Task>?)reconnectedCallbackvoidSubscribe to reconnect success notificationParameter is new connectionId (may be null)
AddListenerClosed(Func<Exception?, Task>)closeCallbackvoidSubscribe to connection close notificationException is null for normal close

3.3 Callback Events

EventCallback ParametersTrigger TimingUsage Notes
AddListenerUpdateInfoUpdateInfoEventArgsInfo.Code, Info.Body (VersionEntry list)After version comparison in standard Client strategyNo update → Code = 404; has update → Body contains VersionEntry list
AddListenerUpdatePrecheckFunc<UpdateInfoEventArgs, bool> — return true to skip (non-forced), false to continueAfter UpdateInfo, before downloadFor disk space check, network check, user confirmation dialog
AddListenerProgressProgressEventArgsProgress (download) or DiffProgress (diff)Download progress or diff progress updatesOnly one of Progress / DiffProgress is non-null per event
AddListenerMultiDownloadCompletedMultiDownloadCompletedEventArgsVersion, IsCompletedSingle asset download completionNot "all downloads complete"
AddListenerMultiAllDownloadCompletedMultiAllDownloadCompletedEventArgsIsAllDownloadCompleted, FailedVersionsAfter all download tasks completeFailure details in FailedVersions
AddListenerMultiDownloadErrorMultiDownloadErrorEventArgsException, VersionSingle download failureRecord failures for display/monitoring
AddListenerMultiDownloadStatisticsMultiDownloadStatisticsEventArgsSpeed, Remaining, BytesReceivedLegacy/compat download statisticsNew code should use AddListenerProgress
AddListenerExceptionExceptionEventArgsException, MessageWhen strategies catch exceptionsNotification only; no automatic retry

UpgradeHubService Push Events:

EventCallback ParametersTrigger TimingUsage Notes
AddListenerReceiveAction<string> — message content (JSON string)When server pushes version updateMessage format determined by server
AddListenerOnlineAction<string> — status descriptionWhen online/offline status changesUse for UI status display
AddListenerReconnectedFunc<string?, Task>? — new connectionIdAfter successful reconnectCan refresh client state
AddListenerClosedFunc<Exception?, Task> — close reason (null = normal)When connection closesUse for logging and cleanup

4. Advanced Examples

4.1 Extension Points Overview

Core provides 10 extension registration methods via AbstractBootstrap, all returning the bootstrap instance for fluent chaining. All registered types must have parameterless constructors.

Extension InterfaceRegistration MethodScope
IUpdateHooksHooks<T>()Update lifecycle hooks
IUpdateReporterUpdateReporter<T>()Status reporting
ISslValidationPolicySslPolicy<T>()HTTPS certificate validation
IHttpAuthProviderHttpAuth<T>()HTTP request authentication
IDownloadSourceDownloadSource<T>()Version list & download source
IDownloadPolicyDownloadPolicy<T>()Download retry/timeout/circuit-breaker
IDownloadExecutorDownloadExecutor<T>()Single file download
IDownloadPipelineDownloadPipeline<T>()Post-download processing
IDownloadOrchestratorDownloadOrchestrator<T>()Batch download orchestration
IStrategyStrategy<T>()Platform-level update strategy

4.2 Examples by Scenario

Scenario 1: Custom Diff Algorithm with Parallelism

Description: Large projects wanting faster client-side patch application with StreamingHdiffDiffer and parallelism 4.

using GeneralUpdate.Core;
using GeneralUpdate.Core.Differential;
using GeneralUpdate.Core.Pipeline;
using GeneralUpdate.Differential.Differ;

await new GeneralUpdateBootstrap()
.SetConfig(request)
.UseDiffPipeline(builder =>
{
builder
.UseDiffer(new StreamingHdiffDiffer())
.WithParallelism(4)
.WithStopOnFirstError(true);
})
.SetOption(Option.PatchEnabled, true)
.SetOption(Option.AppType, AppType.Client)
.LaunchAsync();

Scenario 2: Custom Lifecycle Hooks

Description: Check disk space before update, write logs after update, grant execute permissions on Linux/macOS.

using GeneralUpdate.Core.Hooks;

public sealed class ProductUpdateHooks : IUpdateHooks
{
public Task<bool> OnBeforeUpdateAsync(HookContext ctx)
{
var drive = new DriveInfo(Path.GetPathRoot(ctx.InstallPath)!);
if (drive.AvailableFreeSpace < 500L * 1024 * 1024)
return Task.FromResult(false); // Reject update
return Task.FromResult(true);
}

public Task OnDownloadCompletedAsync(DownloadContext ctx) => Task.CompletedTask;

public Task OnAfterUpdateAsync(HookContext ctx)
{
File.AppendAllText(
Path.Combine(ctx.InstallPath, "update-history.log"),
$"{DateTimeOffset.Now:O} {ctx.CurrentVersion} -> {ctx.TargetVersion}{Environment.NewLine}");
return Task.CompletedTask;
}

public Task OnUpdateErrorAsync(HookContext ctx, Exception ex)
{
File.AppendAllText(Path.Combine(ctx.InstallPath, "update-error.log"), $"{ex}{Environment.NewLine}");
return Task.CompletedTask;
}

public Task OnBeforeStartAppAsync(HookContext ctx) => Task.CompletedTask;
}

await new GeneralUpdateBootstrap()
.SetConfig(request)
.Hooks<ProductUpdateHooks>()
.SetOption(Option.AppType, AppType.Client)
.LaunchAsync();

Scenario 3: Custom Download Source (Private Service/Config Center)

Description: Pull download asset lists from an internal config center instead of the standard version check API.

using GeneralUpdate.Core.Download.Abstractions;
using GeneralUpdate.Core.Download.Models;

public sealed class ConfigCenterDownloadSource : IDownloadSource
{
public async Task<DownloadSourceResult> ListAsync(CancellationToken token = default)
{
var assets = new[]
{
new DownloadAsset(
Name: "MyApp-2.0.0.zip",
Url: "https://cdn.internal.example.com/releases/MyApp-2.0.0.zip",
Size: 50_000_000,
SHA256: "abc123...",
Version: "2.0.0")
};
return new DownloadSourceResult
{
Assets = assets,
HasMainUpdate = true,
HasUpgradeUpdate = false
};
}
}

await new GeneralUpdateBootstrap()
.SetConfig(request)
.DownloadSource<ConfigCenterDownloadSource>()
.SetOption(Option.AppType, AppType.Client)
.LaunchAsync();

Scenario 4: Custom HTTP Authentication

Description: Append JWT Bearer Token to all HTTP requests from Core.

using GeneralUpdate.Core.Security;

public sealed class JwtAuthProvider : IHttpAuthProvider
{
private readonly string _token = Environment.GetEnvironmentVariable("UPDATE_JWT_TOKEN") ?? "";

public Task ApplyAuthAsync(HttpRequestMessage request, CancellationToken token = default)
{
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
return Task.CompletedTask;
}
}

await new GeneralUpdateBootstrap()
.SetConfig(request)
.HttpAuth<JwtAuthProvider>()
.SetOption(Option.AppType, AppType.Client)
.LaunchAsync();

Scenario 5: Silent Update + Process Exit Trigger

Description: Main app polls for updates in background, triggers upgrade on process exit.

using GeneralUpdate.Core;

var bootstrap = new GeneralUpdateBootstrap()
.SetSource(
updateUrl: "https://update.example.com/api/upgrade/verification",
appSecretKey: "your-app-secret")
.SetOption(Option.AppType, AppType.Client)
.SetOption(Option.Silent, true)
.SetOption(Option.SilentPollIntervalMinutes, 30)
.SetOption(Option.LaunchClientAfterUpdate, true)
.AddListenerException((_, e) => Console.WriteLine($"Update error: {e.Message}"));

await bootstrap.LaunchAsync();

// On app exit: launch upgrade if prepared
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{
if (bootstrap.SilentOrchestrator?.HasPreparedUpdate == true)
bootstrap.SilentOrchestrator.TryLaunchUpgrade();
};

Scenario 6: SignalR Real-Time Push + Standard Update

Description: Use UpgradeHubService for server push notifications alongside GeneralUpdateBootstrap for standard updates. Server can push notifications immediately when new versions are available.

using GeneralUpdate.Core;
using GeneralUpdate.Core.Hubs;

// 1. Start SignalR push listener
var hub = new UpgradeHubService(
"http://localhost:5000/UpgradeHub",
appkey: "dfeb5833-975e-4afb-88f1-6278ee9aeff6");

hub.AddListenerReceive(async (message) =>
{
Console.WriteLine($"Push notification: {message}");
// Trigger update check or notify user in UI
});

hub.AddListenerOnline((info) =>
Console.WriteLine($"Online status: {info}"));

hub.AddListenerReconnected((connectionId) =>
{
Console.WriteLine($"Reconnected, connectionId={connectionId}");
return Task.CompletedTask;
});

hub.AddListenerClosed((exception) =>
{
Console.WriteLine(exception != null
? $"Connection closed abnormally: {exception.Message}"
: "Connection closed normally");
return Task.CompletedTask;
});

await hub.StartAsync();

// 2. Standard update flow
await new GeneralUpdateBootstrap()
.SetSource(
updateUrl: "https://update.example.com/api/upgrade/verification",
appSecretKey: "your-app-secret")
.SetOption(Option.AppType, AppType.Client)
.AddListenerException((_, e) => Console.WriteLine(e.Exception))
.LaunchAsync();

// 3. Cleanup on exit
// await hub.StopAsync();
// await hub.DisposeAsync();

Scenario 7: DI Container Registration for UpgradeHubService

Description: Register IUpgradeHubService in Prism / Generic Host / ASP.NET Core DI containers.

using GeneralUpdate.Core.Hubs;

// Prism example
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IUpgradeHubService, UpgradeHubService>();
}

public MainWindowViewModel(IUpgradeHubService hubService)
{
hubService.AddListenerReceive((message) =>
Console.WriteLine($"Push: {message}"));
_ = hubService.StartAsync();
}

// Generic Host / ASP.NET Core example
builder.Services.AddSingleton<IUpgradeHubService>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return new UpgradeHubService(
config["HubUrl"]!,
appkey: config["AppSecretKey"]);
});

5. Basic Usage Examples

5.1 Quick Start (Minimal Demo)

Minimal config using manifest for identity auto-discovery:

using GeneralUpdate.Core;

await new GeneralUpdateBootstrap()
.SetSource(
updateUrl: "https://update.example.com/api/upgrade/verification",
appSecretKey: "your-app-secret")
.SetOption(Option.AppType, AppType.Client)
.LaunchAsync();

Upgrade process entry point (Update.exe):

await new GeneralUpdateBootstrap()
.SetOption(Option.AppType, AppType.Upgrade)
.AddListenerException((_, e) => Console.WriteLine(e.Exception))
.LaunchAsync();

5.2 Basic Parameter Combination

using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;

var request = new UpdateRequest
{
UpdateUrl = "https://update.example.com/api/upgrade/verification",
ReportUrl = "https://update.example.com/api/upgrade/report",
UpdateAppName = "UpgradeSample.exe",
MainAppName = "ClientSample.exe",
InstallPath = AppDomain.CurrentDomain.BaseDirectory,
ClientVersion = "1.0.0",
AppSecretKey = "your-app-secret",
ProductId = "your-product-id"
};

await new GeneralUpdateBootstrap()
.SetConfig(request)
.SetOption(Option.AppType, AppType.Client)
.SetOption(Option.DiffMode, DiffMode.Parallel)
.SetOption(Option.MaxConcurrency, 4)
.SetOption(Option.PatchEnabled, true)
.AddListenerProgress((_, e) =>
{
if (e.Progress != null)
Console.WriteLine($"{e.Progress.AssetName}: {e.Progress.Percentage:F1}%");
})
.AddListenerException((_, e) => Console.WriteLine(e.Exception))
.LaunchAsync();

5.2.1 SignalR Real-Time Push Quick Start

using GeneralUpdate.Core.Hubs;

// Create push client
var hub = new UpgradeHubService(
"http://localhost:5000/UpgradeHub",
appkey: Guid.NewGuid().ToString());

// Subscribe to push messages
hub.AddListenerReceive((message) =>
{
Console.WriteLine($"Push notification: {message}");
});

// Establish connection
await hub.StartAsync();

Console.WriteLine("Connected, waiting for server push...");
Console.ReadLine();

// Stop connection (retain reconnect ability)
await hub.StopAsync();

// Release resources (cannot be reused)
await hub.DisposeAsync();

5.3 Production-Ready Example

Full Client-side update with events, diff pipeline, concurrency control, and status reporting:

using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;
using GeneralUpdate.Core.Pipeline;
using GeneralUpdate.Core.Models;
using GeneralUpdate.Core.Download;
using GeneralUpdate.Differential.Differ;

var request = new UpdateRequestBuilder()
.SetUpdateUrl("https://update.mycompany.com/api/upgrade/verification")
.SetReportUrl("https://update.mycompany.com/api/upgrade/report")
.SetUpgradeAppName("MyApp.Upgrade.exe")
.SetMainAppName("MyApp.exe")
.SetClientVersion("1.0.0")
.SetUpgradeClientVersion("1.0.0")
.SetAppSecretKey("prod-secret-key")
.SetProductId("my-product")
.SetInstallPath(AppDomain.CurrentDomain.BaseDirectory)
.Build();

var bootstrap = new GeneralUpdateBootstrap()
.SetConfig(request)
.SetOption(Option.AppType, AppType.Client)
.SetOption(Option.DiffMode, DiffMode.Parallel)
.SetOption(Option.MaxConcurrency, 4)
.SetOption(Option.DownloadTimeout, 120)
.SetOption(Option.PatchEnabled, true)
.SetOption(Option.BackupEnabled, true)
.SetOption(Option.VerifyChecksum, true)
.SetOption(Option.RetryCount, 5)
.SetOption(Option.RetryInterval, TimeSpan.FromSeconds(2))
.UseDiffPipeline(builder => builder
.UseDiffer(new StreamingHdiffDiffer())
.WithParallelism(4))
.AddListenerUpdateInfo((_, e) =>
{
Console.WriteLine(e.Info?.Code == "404"
? "Already up to date."
: $"Found {e.Info?.Body?.Count ?? 0} version(s).");
})
.AddListenerProgress((_, e) =>
{
if (e.Progress != null)
Console.WriteLine($"[Download] {e.Progress.AssetName}: {e.Progress.Percentage:F1}%");
if (e.DiffProgress != null)
Console.WriteLine($"[Patch] {e.DiffProgress.CurrentFile}: {e.DiffProgress.Completed}/{e.DiffProgress.Total}");
})
.AddListenerMultiAllDownloadCompleted((_, e) =>
{
Console.WriteLine(e.IsAllDownloadCompleted
? "All downloads completed."
: $"Failed: {e.FailedVersions.Count}");
})
.AddListenerException((_, e) => Console.WriteLine($"Error: {e.Message}"));

await bootstrap.LaunchAsync();

5.4 OSS Object Storage Update (Zero Server Deployment)

Scenario: The OSS (Object Storage Service) update mode is for deployments without a backend service: host the version manifest and update packages directly on an object storage service (Aliyun OSS / AWS S3 / MinIO, etc.), and the client performs version checking and updates through a static versions.json file — no server-side API is required.

Workflow:

Object Storage Bucket (Aliyun OSS / AWS S3 / MinIO)
├── versions.json ← Version manifest (static file)
└── packet_20250102230201638_1.0.0.1.zip ← Update package
StepRoleDescription
1OpsUpload versions.json and update packages to the object storage bucket
2OssClient (main app)Downloads versions.json from UpdateUrl on startup, picks the latest version by PubTime descending
3OssClient (main app)Compares the latest version with ClientVersion (SemVer 2.0); ends if up to date
4OssClient (main app)If a new version is found → launches the upgrade process → the main app exits itself
5OssUpgrade (upgrade app)Reads the version manifest, downloads every package newer than the current version, extracts and overwrites the install directory
6OssUpgrade (upgrade app)Writes the version back to generalupdate.manifest.json → launches the main app → exits itself

How the two processes cooperate:

OSS mode requires two separate executables, and each process's role is determined by the AppType in its own code — not passed via command-line arguments or an IPC file:

ExecutableProject codeAppTypeResponsibility
Main app (e.g. MyApp.exe)Your business appAppType.OssClientCheck version, launch the upgrade process, exit
Upgrade app (e.g. UpgradeApp.exe)A separate small projectAppType.OssUpgradeDownload packages, extract & install, launch the main app, exit

When OssClient finds a new version, it starts the upgrade app via Process.Start with no arguments — the upgrade app knows it must perform the update from its own AppType.OssUpgrade code and obtains the main app identity from generalupdate.manifest.json in the install directory (see "Install directory layout" below).

versions.json manifest format:

[
{
"PacketName": "packet_20250102230201638_1.0.0.1",
"Hash": "ad1a85a9169ca0083ab54ba390e085c56b9059efc3ca8aa1ec9ed857683cc4b1",
"Version": "1.0.0.1",
"Url": "https://your-bucket.example.com/packages/packet_20250102230201638_1.0.0.1.zip",
"PubTime": "2025-01-02T23:48:21"
}
]
FieldTypeDescription
PacketNamestringUpdate package name, used to derive the local archive file name ({PacketName}.zip)
HashstringSHA256 hash of the update package, verified against the downloaded file
VersionstringVersion number (SemVer 2.0 format), used for version comparison
UrlstringDownload URL of the update package (public or pre-signed object storage URL)
PubTimeDateTimePublish time; the OSS mode picks the latest version by this field in descending order
Manifest file naming

After download, versions.json is saved into the install directory as {MainAppName}_versions.json, and the upgrade process reads it from the same location. The main app and the upgrade app must use consistent MainAppName / UpdateAppName configuration.

Install directory layout (main app + upgrade app + manifest):

Install directory (InstallPath)
├── MyApp.exe ← Main app (AppType.OssClient)
├── MyApp.dll / resources...
├── generalupdate.manifest.json ← App identity manifest (example below)
└── update/
└── UpgradeApp.exe ← Upgrade app (AppType.OssUpgrade)

Corresponding generalupdate.manifest.json example (identity fields can also all be set in code; when both exist, the manifest wins):

{
"mainAppName": "MyApp.exe",
"clientVersion": "1.0.0.0",
"appType": "OssClient",
"updateAppName": "UpgradeApp.exe",
"updatePath": "update/"
}
  • The upgrade app must reside in InstallPath or the directory specified by UpdatePath; otherwise OssClient cannot launch it and throws FileNotFoundException
  • The upgrade app reads {MainAppName}_versions.json (downloaded by OssClient into InstallPath) and obtains MainAppName / UpdateAppName / ClientVersion identity from generalupdate.manifest.json
  • If the upgrade app is placed in an update/ subdirectory (its base directory is the subdirectory) while the manifest lives in the parent directory, the upgrade app must point to the parent explicitly via SetSource(..., installPath: ...), e.g. Path.GetFullPath(Path.Combine(baseDir, ".."))

UpdateRequest fields in OSS mode:

FieldRequiredDescription
UpdateUrlYesPublic URL of versions.json; OssClient downloads the version manifest from it on startup
MainAppNameRecommendedMain app executable name; determines the local manifest file name ({MainAppName}_versions.json) and is the target launched after the upgrade completes
UpdateAppNameRecommendedUpgrade app executable name; the process launched by OssClient when a new version is found; defaults to Update.exe
ClientVersionRecommendedCurrent main app version (SemVer 2.0 format), used for version comparison and package filtering
InstallPathOptionalApplication install root directory, defaults to AppDomain.CurrentDomain.BaseDirectory; where versions.json is saved and update packages are extracted
UpdatePathOptionalDirectory of the upgrade app, defaults to InstallPath; OssClient resolves the upgrade executable from this directory first
Fields not needed in OSS mode

OSS mode has no server-side API, so the following auth / reporting / logging related fields are not required (they are ignored if set): AppSecretKey, ReportUrl, UpdateLogUrl, Token, AuthScheme, BasicUsername, BasicPassword, Bowl.

In addition, the following Option runtime options are used in OSS mode:

OptionDefaultDescription
Option.AppTypeRequired, sets the current process role: OssClient (main app) or OssUpgrade (upgrade app)
Option.EncodingUTF8Character encoding used when extracting update packages
Option.DownloadTimeout60Download timeout for update packages (seconds)

Main app example (AppType.OssClient):

using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;

var request = new UpdateRequest
{
UpdateUrl = "https://your-bucket.example.com/packages/versions.json",
UpdateAppName = "OSSUpgradeSample.exe",
MainAppName = "OSSClientSample.exe",
ClientVersion = "1.0.0.0",
InstallPath = AppDomain.CurrentDomain.BaseDirectory
};

await new GeneralUpdateBootstrap()
.SetConfig(request)
.SetOption(Option.AppType, AppType.OssClient)
.AddListenerException((_, e) => Console.WriteLine(e.Exception))
.LaunchAsync();

Upgrade app example (AppType.OssUpgrade):

using GeneralUpdate.Core;

// The upgrade app needs no SetConfig: identity fields such as MainAppName /
// UpdateAppName / ClientVersion are auto-discovered from generalupdate.manifest.json
// in the install directory (AppMetadataDiscoverer).
// If the upgrade app lives in an update/ subdirectory (manifest in the parent),
// point to the parent explicitly:
//
// var baseDir = AppDomain.CurrentDomain.BaseDirectory;
// await new GeneralUpdateBootstrap()
// .SetSource("https://your-bucket.example.com/packages/versions.json", "",
// installPath: Path.GetFullPath(Path.Combine(baseDir, "..")))
// .SetOption(Option.AppType, AppType.OssUpgrade)
// .LaunchAsync();

await new GeneralUpdateBootstrap()
.SetOption(Option.AppType, AppType.OssUpgrade)
.AddListenerException((_, e) => Console.WriteLine(e.Exception))
.LaunchAsync();
Getting started in 5 steps
  1. Create the main app project: reference NuGet package GeneralUpdate.Core, write code as in the "Main app example" above (AppType.OssClient), and set UpdateAppName to your upgrade app file name
  2. Create the upgrade app project: a separate small project, write code as in the "Upgrade app example" above (AppType.OssUpgrade); the build output file name must match UpdateAppName
  3. Prepare the manifest: generate generalupdate.manifest.json with GeneralUpdate.Tools (or write it manually as in the example above) and place it in the install directory together with both executables
  4. Publish an update package: zip your application files and upload to object storage; write and upload versions.json (Hash = SHA256 of that zip, Url = accessible URL of the zip)
  5. Verify: run the main app and watch the logs to confirm it downloads versions.json, detects the new version, launches the upgrade app, and completes the install

Effect & caveats

  • UpdateUrl points directly to the public URL of versions.json, and update packages are also hosted on object storage — no Verification / Report API is needed
  • Keep the bucket private and use pre-signed URLs; if it must be public-read, do not store sensitive content
  • OSS mode does not distinguish main-app vs. upgrade-app packages: every package in versions.json newer than the current version is downloaded and applied in sequence
  • Do not include the component's internal dependency assemblies (e.g. System.Text.Json.dll, Microsoft.Bcl.AsyncInterfaces.dll) in the update package; alternatively exclude them via the Files / Formats / Directories skip configuration
  • Use SemVer 2.0 version numbers (e.g. 1.0.0.0), otherwise version comparison fails
  • Same as the standard mode, OSS mode supports generalupdate.manifest.json identity discovery, Hooks<T>() lifecycle hooks, UpdateReporter<T>() status reporting, and custom DownloadSource<T>() / DownloadOrchestrator<T>()

6. Global Configuration

Manifest Configuration

{
"mainAppName": "ClientSample.exe",
"clientVersion": "1.0.0",
"appType": "Client",
"updateAppName": "UpgradeSample.exe",
"upgradeClientVersion": "1.0.0",
"productId": "sample-product",
"updatePath": "update/"
}

Configuration Priority

PrioritySourceDescription
1 (Highest)Code: SetConfig(UpdateRequest) or SetSource(...)Overrides all other sources
2generalupdate.manifest.json fieldsAuto-fills fields not explicitly set in code
3 (Lowest)Component internal defaultsUpdateAppName = "Update.exe", InstallPath = BaseDirectory, etc.

Version Write-Back

After a successful update, Core automatically writes back the version to generalupdate.manifest.json:

ScenarioWrite-Back Field
Main app update completesClientVersion
Upgrade process update completesUpgradeClientVersion

Logging Configuration

using GeneralUpdate.Core;

// Disable logging (performance-sensitive scenarios)
GeneralTracer.SetTracingEnabled(false);

// Re-enable (troubleshooting)
GeneralTracer.SetTracingEnabled(true);

// Release logging resources
GeneralTracer.Dispose();