基于 JSON 配置的 .NET 桌面应用自动更新实现指南

0 阅读3分钟

基于 JSON 配置的 .NET 桌面应用自动更新实现指南

在现代桌面应用开发中,自动更新能力已成为提升用户体验和保障软件安全的关键功能。对于使用 .NET(如 WinForms、WPF 或 MAUI)构建的 Windows 桌面应用,通过轻量级的 JSON 配置文件驱动更新逻辑,是一种灵活、可维护且易于部署的方案。本文将详细介绍如何设计并实现一个基于 JSON 配置的自动更新系统。


一、方案设计思路

我们的目标是:无需依赖复杂框架(如 Squirrel、ClickOnce),仅通过 HTTP 请求 + JSON 配置 + 简单下载逻辑,实现版本检测与增量/全量更新

核心组件包括:

  1. 远程 JSON 配置文件:托管在服务器或 CDN 上,描述最新版本信息。
  2. 本地版本管理:读取当前应用版本(通常来自 AssemblyVersion)。
  3. 更新检查器:对比本地与远程版本,判断是否需要更新。
  4. 下载与安装模块:下载新版本安装包,并启动更新流程(如替换文件或运行安装程序)。
  5. 用户交互界面(可选):提示用户有新版本可用。

二、JSON 配置文件结构

在服务器上维护一个公开可访问的 update.json 文件,例如:

{
  "version": "2.1.0",
  "releaseNotes": "修复了若干稳定性问题,新增深色主题支持。",
  "downloadUrl": "https://example.com/app/myapp-v2.1.0.exe",
  "mandatory": false,
  "minSupportedVersion": "1.0.0"
}

字段说明:

  • version:语义化版本号(SemVer),用于版本比较。
  • downloadUrl:新版本安装包或 ZIP 包的下载地址。
  • releaseNotes:更新日志,用于展示给用户。
  • mandatory:是否强制更新(如存在严重安全漏洞)。
  • minSupportedVersion:最低支持版本,用于判断是否需跳过多个版本。

三、读取本地应用版本

在 .NET 中,可通过以下方式获取当前程序集版本:

var currentVersion = Assembly.GetExecutingAssembly()
    .GetName().Version?.ToString() ?? "1.0.0";

💡 建议使用 [assembly: AssemblyVersion("2.1.0")]AssemblyInfo.cs 中显式指定版本,或通过项目文件 <Version> 属性控制。


四、实现更新检查逻辑

1. 定义更新信息模型

public class UpdateInfo
{
    public string Version { get; set; } = string.Empty;
    public string DownloadUrl { get; set; } = string.Empty;
    public string ReleaseNotes { get; set; } = string.Empty;
    public bool Mandatory { get; set; }
    public string MinSupportedVersion { get; set; } = "0.0.0";
}

2. 从远程加载 JSON 并解析

private async Task<UpdateInfo?> CheckForUpdateAsync(string updateUrl)
{
    try
    {
        using var client = new HttpClient();
        var json = await client.GetStringAsync(updateUrl);
        return JsonSerializer.Deserialize<UpdateInfo>(json);
    }
    catch (Exception ex)
    {
        // 记录日志:无法获取更新信息
        Console.WriteLine($"Update check failed: {ex.Message}");
        return null;
    }
}

3. 版本比较

使用 Version 类进行语义化比较:

bool IsNewerVersion(string current, string latest)
{
    return Version.TryParse(current, out var v1) &&
           Version.TryParse(latest, out var v2) &&
           v2 > v1;
}

⚠️ 注意:Version 类要求格式为 x.x[.x[.x]],不支持 -beta 等后缀。若需完整 SemVer 支持,可引入 Semver NuGet 包。


五、执行更新流程

方案 A:下载安装程序并启动(推荐)

适用于 .exe.msi 安装包:

private async Task DownloadAndInstallAsync(string downloadUrl)
{
    var tempFile = Path.Combine(Path.GetTempPath(), "MyApp_Update.exe");
    
    using var client = new HttpClient();
    using var stream = await client.GetStreamAsync(downloadUrl);
    using var fileStream = File.Create(tempFile);
    await stream.CopyToAsync(fileStream);

    // 启动安装程序,并退出当前应用
    Process.Start(new ProcessStartInfo
    {
        FileName = tempFile,
        UseShellExecute = true
    });
    
    Application.Current.Shutdown(); // WPF
    // 或 Application.Exit(); // WinForms
}

方案 B:解压 ZIP 替换文件(无安装程序场景)

适用于绿色版应用:

  1. 下载 ZIP 包;
  2. 关闭主程序;
  3. 启动一个独立的“更新器”进程(避免文件被占用);
  4. 更新器解压 ZIP 到应用目录,再重启主程序。

🔒 安全提示:务必验证下载内容的完整性(如 SHA256 校验),防止中间人攻击。


六、集成到应用生命周期

在应用启动时(或定期后台任务)触发检查:

// WPF 示例:App.xaml.cs
protected override async void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    
    var updateInfo = await CheckForUpdateAsync("https://example.com/update.json");
    if (updateInfo != null && IsNewerVersion(GetCurrentVersion(), updateInfo.Version))
    {
        var result = MessageBox.Show(
            $"发现新版本 {updateInfo.Version}!\n{updateInfo.ReleaseNotes}\n\n是否立即更新?",
            "更新可用", 
            MessageBoxButton.YesNo);
        
        if (result == MessageBoxResult.Yes)
        {
            await DownloadAndInstallAsync(updateInfo.DownloadUrl);
        }
    }
}

七、增强建议

  1. 缓存机制:避免每次启动都请求,可记录上次检查时间(如 24 小时内不再检查)。
  2. HTTPS 强制:确保 update.json 和安装包通过 HTTPS 提供。
  3. 数字签名验证:对安装包进行 Authenticode 签名,提升安全性。
  4. 静默更新(可选) :后台下载,下次启动时自动应用。
  5. 回滚机制:保留旧版本备份,更新失败可恢复。

八、总结

通过一个简单的 JSON 配置文件,配合 .NET 内置的 HTTP、JSON 和进程管理能力,我们可以快速构建一个轻量、可控、跨 .NET 桌面平台的自动更新系统。该方案无需外部依赖,易于调试和部署,特别适合中小型项目或对更新流程有定制需求的场景。

📌 最佳实践:将更新逻辑封装为独立类库(如 MyApp.Updater),便于复用和测试。

随着 .NET 生态的演进,未来也可考虑与 MSIX 打包结合,但基于 JSON 的方案因其简单性和通用性,仍具有不可替代的价值。