最近在给团队桌面客户端做自动升级改造,从选型、接入到上线折腾了差不多一周,把 .NET 生态里几个开源跨平台方案都过了一遍。结论先放在这里:如果项目是 .NET 6+、需要同时覆盖 Windows / macOS / Linux,自动升级这块不用再自己造轮子,直接用开源组件是性价比最高的路线。但选型只是开始,真正坑人的是接入后的各种边界情况——文件占用、版本目录切换、通道不一致、杀毒软件误报,这些文档里往往不会写得太细。
这篇文章就是把"一款基于 .NET 的开源跨平台自动升级组件"从原理到落地完整复盘一遍:解决什么问题、核心设计思路、实测过的接入步骤、以及我踩过的高频坑。适合正在为 WPF / WinForms / Avalonia / MAUI 应用寻找更新方案的开发者参考,无论你是第一次接自动升级,还是已经接了一半被各种问题卡住,这篇都能给你一份可抄的作业。
1. 为什么 .NET 桌面应用自动升级不能照搬 Web 思路
1.1 文件占用、覆盖顺序与崩溃风险的三个硬伤
很多人第一次做桌面端自动升级时,第一反应是"启动的时候检测到新版本,就去下载一个 exe,提示用户自己装"。这其实不是真正的自动升级,只是把下载链接放进了应用里。真正的自动升级组件要解决的问题要麻烦得多。
第一个硬伤是文件占用。Web 应用发版是替换服务器上的文件,服务器上的旧进程早就停了。但桌面端应用更新时,用户很可能正在运行旧版本,exe、dll 被进程锁住,Windows 下根本删不掉。你总不能跟用户说"请先关掉程序再更新"——那产品体验基本就废了。
第二个硬伤是覆盖顺序。桌面应用不是一个单文件,而是一堆程序集、资源文件、配置文件。新版本文件替换旧版本文件,如果替换到一半进程崩溃或者网络断了,程序可能处于"新旧版本文件混在一起"的状态,轻则启动报错,重则直接打不开。没有原子性保证的更新方案,等于把用户机器当成实验环境。
第三个硬伤是失败后的恢复。Web 发版失败最多回滚服务端,用户无感知。桌面端如果更新完应用起不来,用户看到的就是一个打不开的软件。如果更新前没有保留旧版本、没有回滚机制,就只能等开发者在论坛里被骂,然后手把手教用户重新安装。
我习惯用一个类比:你不能让一架正在飞行的飞机把发动机拆了换新的。你得先让它降落到停机坪,完成新旧切换,再重新起飞。自动升级组件做的就是"停机坪"这层保障工作,而不是单纯地把文件从 A 点搬到 B 点。
1.2 从 ClickOnce 到 Velopack:自动升级组件演进脉络
. NET 生态里做自动升级的方案不少,但不同年代的方案解决的问题差很远。很早以前大家用 ClickOnce,它在 Visual Studio 里配置简单、由框架托管,但只能在 Windows 上用,而且部署形态非常受限。后来社区里出现了 Squirrel.Windows 这类方案,核心思路是"安装时生成版本化目录 + 通过快捷方式切换当前版本",比直接覆盖文件靠谱得多。再往后 Squirrel 系逐步演进,出现了 Velopack 这类同时支持 Windows / macOS / Linux 的跨平台组件,正好踩中了 .NET 跨平台应用的更新需求。
我在选型时画过一张对比表,这里直接贴出来供参考:
| 方案 | 平台支持 | 开源 | 差分更新 | 是否需要自建服务端 | 适用场景 |
|---|---|---|---|---|---|
| ClickOnce | 仅 Windows | 否(内置) | 不支持 | 可配合 IIS | 老牌 Windows 内部工具 |
| Squirrel.Windows | 仅 Windows | 是 | 有限支持 | 需要 | 老牌 Windows 桌面应用 |
| AutoUpdater.NET | 仅 Windows | 是 | 有限支持 | 需要 | 简单 WinForms / WPF |
| NetSparkle | Windows/macOS/Linux | 是 | 不支持 | 需要 | 需要自定义更新源场景 |
| Velopack | Windows/macOS/Linux | 是 | 支持 | 需要 | 跨平台 .NET 桌面应用 |
Velopack 是我最终选定的方案。选它不是因为功能最全,而是因为它把 Squirrel 时代的"目录切换 + 快捷方式更新"思路继承
