做.NET桌面应用开发,只要产品不是只在自己电脑上跑,自动更新就是个绕不开的坎。我维护过好几个客户端项目,最难受的阶段不是写业务功能,而是每次发新版都要在群里喊“请大家手动覆盖安装一下”,然后被各种问题追着跑:有人没权限替换文件,有人旧进程没退出导致安装失败,还有人连不上共享目录。这些事单看都不复杂,但叠加在一两百台机器上,就是场灾难。所以后来我把“自动更新”正式当成一项工程来做,这篇就把我用过的、研究过的几类 .NET 桌面程序更新方案一次性梳理完,并讲清楚每种方案背后的取舍和适用场景。
在聊具体技术之前,先说清楚一个观点:自动更新不仅仅是给程序加一个“下载新版并覆盖”的功能,它实际上是一套“从服务端到客户端”的发布流程。你只有把更新当作一个完整的发布系统来设计,才能避开后面要踩的坑。下面我从为什么手动更新容易出问题讲起,再到现成框架、自研设计、发布灰度,把整条链路走一遍。
1. 手动发安装包的日子,先让我看清了问题出在哪
1.1 三个最典型的翻车现场
先说一个最经典的场景:有用户反馈程序报“找不到 xxx.dll”,我一查,发现他原来的安装目录里根本没有我新版依赖的那个文件。原因很简单,我更新包里的 dll 列表是新的,但用户在共享目录里拖过去覆盖的时候,系统的文件占用、杀毒软件拦截等任何一个环节出问题,都会导致旧文件残留、新文件缺失,最终得到一个“既不是旧版也不是新版”的混合目录。
第二个场景是文件被占用。Windows 下如果一个程序进程还在运行,它的 exe 和 dll 默认会被锁定,普通复制操作直接报“文件正在被另一进程使用”。用户如果开着程序、点完下载后没关主程序,就执行覆盖安装,大概率会失败。
第三个场景和权限有关。如果应用装在 C:\Program Files 目录,普通用户没有写权限。很多内部系统为了图省事,直接让用户右键“管理员身份运行”安装包,UAC 弹窗又是阻碍,稍微不懂电脑的同事就会卡在那里。
这些表面上是操作问题,本质上暴露了一个事实:手动分发模式没有任何“可验证性”和“可靠性”。你无法确认用户最后跑起来的到底是哪个版本的哪个文件。
1.2 自动更新的本质:可靠送达、安全替换、失败恢复
把上面的问题抽象一下,桌面端自动更新要解决的核心问题就三条。
第一,可靠送达。新版文件要从某个服务器或共享源下载到本地,网络中断、下载到一半、源地址变更等情况都要处理好。第二,安全替换。全新版本的文件不能“散装”覆盖旧目录,因为覆盖过程中可能崩溃,一旦中途失败,程序既不是旧版也不是新版,后续再跑就可能报错。第三,失败恢复。更新后如果新版本启动不了,用户不应该被困在一个“死掉的新版本”里,系统要能回滚到旧版本。
理解这三条,很多配置和设计就有了方向。比如选择辅助进程更新而不是在主程序里直接覆盖,是为了“安全替换”;每次下载后做哈希校验,是为了“可靠送达”;保留上一版本的备份,是为了“失败恢复”。
1.3 一次理想更新流程应该长什么样
我后来做更新功能,总会把流程画成一条固定链路:客户端启动或点击“检查更新”时,先向服务器请求更新清单,把自己的当前版本号发过去;服务器返回“有哪些文件需要下载、每个文件的下载地址和哈希值”;客户端下载到临时目录,逐文件校验;校验通过后退出主程序;启动一个独立的更新辅助进程,由辅助进程完成“备份旧版 -> 替换文件 -> 启动新版本”;新版启动成功后反馈结果,启动失败则触发回滚。
这套流程每个环节后面都会展开讲。先记住一个关键点:更新动作最好由“独立的辅助程序”完成,而不是让正在运行的主程序自己覆盖自己的 exe,因为 Windows 对正在运行的文件有独占锁,自己替换自己几乎一定会失败。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 不想从零造轮子,可以先看看这四类现成框架
如果你不是想研究底层原理,而是希望项目尽快用上自动更新,最简单的做法是选一个成熟框架。现在我常用的、实践验证过的主流派别有 ClickOnce、Squirrel.Windows / Clowd.Squirrel、Velopack 和 MSIX 应用打包,各有各的脾气。
2.1 ClickOnce:微软自家老牌方案,适合不太复杂的小工具
很多老 .NET 项目最先接触的就是 ClickOnce。它在 Visual Studio 里直接“发布”就能生成部署包,客户端启动时会检查服务器上的清单文件,如果版本号变更就自动下载并安装。代码里也可以用 ApplicationDeployment 的 API 实现更新检查。
它的优点是接入成本极低,不需要你写服务端接口,不需要自己处理证书签名,微软把整个链路都包了。比较适合内部小工具、低频使用且功能不复杂的桌面程序。但它有比较明显的边界:一是部署路径基本在用户目录下,你没法直接把程序装到 Program Files 那种需要管理员权限的位置;二是很多定制化行为受限,比如自定义更新界面、强制
