ClickOnce 是什么 - 从实务角度整理其原理、更新机制以及适用与不适用的场景

· · Windows, 发布, ClickOnce, .NET, Windows开发

谈到 Windows 上 .NET 桌面应用的发布话题,在 MSI 和 MSIX 的阴影下,总会不起眼地反复出现一个名字,那就是 ClickOnce。

不过,如果在这里简单粗暴地

  • 认为它是老技术所以不再使用
  • 或者反过来,觉得它看起来简单,什么都能用 ClickOnce 解决

倾向于其中任何一种极端判断,大都会判断失误。

ClickOnce 并不是什么都能做的万能 installer。但另一方面,在社内业务用的 .NET 应用中,如果场景是 要发布给标准用户,并希望以低成本连更新一起运转,那么 ClickOnce 至今仍相当有力。

本文将以实务视角,配合较多的 Mermaid 图,整理 ClickOnce 究竟是什么、如何运作、优势何在,以及在哪些地方会显得力不从心。内容主要以 2026 年 4 月时点可确认的 Microsoft Learn 信息为前提。

图为概念图。在支持 Mermaid 的 Markdown 环境中会以图形方式显示。

目录

  1. 先说结论
  2. ClickOnce 是什么
  3. 构成 ClickOnce 的要素
  4. 从安装到启动的流程
  5. 更新机制
  6. ClickOnce 的优势
  7. 适用场景
  8. 不适用场景
  9. 实务中容易踩坑的地方
  10. 总结
  11. 相关文章
  12. 参考资料

1. 先说结论

如果用一句话概括 ClickOnce,那就是:一种按用户单位轻松发布 .NET Windows 桌面应用,并包含自动更新在内的发布技术

适用的场景,例如以下这些情况。

  • WinForms / WPF 等社内业务应用
  • 想以标准用户身份安装
  • per-user 发布已经足够
  • 想拥有内置(built-in)更新能力
  • 发布渠道只需网站或文件共享即可

反过来,如果有以下这类需求,最好从一开始就考虑别的方案,会更安全。

  • 面向所有用户的 machine-wide install
  • Windows service、driver、in-process shell extension、深度 COM 注册
  • 需要 package identity
  • 想掌控更新渠道、分阶段发布、自定义 rollback UX
  • 作为 installer 需要深入 OS 的职责很重

总而言之,ClickOnce 是一种 在简单发布与简单更新方面很强,但不适合 OS 集成需求较重的项目 的方式。

先用一张图看清定位

想发布 Windows 应用是否需要深度集成 OS从 MSI / MSIX 方向考虑是 .NET 的 Windows 桌面应用,且 per-user 发布是否足够是否想发布给标准用户built-in 更新是否足够ClickOnce 较为有力也比较一下 xcopy / 自制 updater

2. ClickOnce 是什么

ClickOnce 是 Microsoft 提供的一种 Windows 应用发布技术。官方说明将其定义为:一种可通过最少的用户操作完成安装与运行,并具备自我更新能力的 Windows 应用发布技术

这里重要的是,不要把 ClickOnce 单纯看作”一种 installer 类型”。

实际上,ClickOnce 是一种统一负责以下事务的 发布模型

  • 要发布哪个版本
  • 该版本包含哪些文件
  • 如何检测更新
  • 从哪里获取更新
  • 如何在每个用户专属的安全位置保存
  • 启动时如何确认完整性

ClickOnce 的主体并不是 setup.exe 本身,把它看作 以清单为核心,统一处理发布、更新和缓存管理的机制,会更贴近其本质。

在当前的 .NET 中,ClickOnce 本身依然是一个普通的候选方案。Visual Studio 针对 .NET Core 3.1 及 .NET 5 以后的版本使用 Publish 工具,如果需要手动处理清单,则要使用 dotnet-mage.exe

ClickOnce 所负责的职责

ClickOnce发布哪个版本从哪里获取更新保存在每个用户专属的安全位置确认完整性后启动

3. 构成 ClickOnce 的要素

要理解 ClickOnce 的机制,抓住以下 4 点是最快的途径。

要素 作用
部署清单 (.application) 表示当前应发布的版本、更新位置、更新方式等
应用程序清单 (*.exe.manifest) 表示该版本的应用本体、依赖文件、哈希、入口点等
应用程序文件 exedll、config、数据文件等
setup.exe(可选) 用于确认并安装前提条件的 bootstrapper,在需要特定运行时或依赖组件时使用

这里的核心是 两种清单

  • 部署清单 表示”这个应用目前正确的版本是哪一个”
  • 应用程序清单 表示”该版本的具体内容是什么”

也就是说,ClickOnce 的更新判定首先从部署清单开始,而实际要下载什么内容,则由应用程序清单决定。

4 个要素之间的关系

setup.exe可选确认 / 安装前提条件部署清单 (.application)发布哪个版本更新位置 / 更新条件应用程序清单 (*.exe.manifest)该版本的内容文件列表 / 哈希 / 入口点应用本体exe / dll / config / dataClickOnce 缓存per-user / per-application

setup.exe 并非主角,而是辅助角色

setup.exe 经常很显眼,但它并不是 ClickOnce 的主角,而是 负责确认并安装前提条件的辅助角色

例如,当需要正确版本的 .NET 运行时或额外的可再发行组件时,setup.exe 会先把这些准备好,之后才进入 ClickOnce 本体的发布流程。

4. 从安装到启动的流程

把 ClickOnce 的流程按实务角度大胆简化,就是这样的。

  1. 用户打开 Web 页面或文件共享上的 setup.exe.application
  2. 如果使用 setup.exe 的配置,会先确认前提条件并安装缺失部分
  3. ClickOnce 读取部署清单
  4. 读取部署清单所指向的应用程序清单
  5. 获取所需文件,并放置到每个用户专属的 ClickOnce 缓存中
  6. 如果配置为支持离线使用,会注册到开始菜单或应用列表中
  7. 之后从 ClickOnce 的管理之下启动应用

重点是:这并不是像普通 installer 那样把文件放进 Program Files 的思路

ClickOnce 应用会进入 每个用户专属的安全缓存区域,并按应用和用户分别隔离。这是 ClickOnce 的一大特点。

从安装到首次启动

应用本体ClickOnce 缓存应用程序清单部署清单setup.exe / .application用户应用本体ClickOnce 缓存应用程序清单部署清单setup.exe / .application用户使用 setup.exe 的配置中会先进行前提条件确认打开获取并读取参照目标版本获取所需文件并确认完整性部署并启动

仅在线使用与支持离线使用

ClickOnce 大致有两种呈现方式。

  • 仅在线使用:以发布位置为起点执行的形式,常驻安装的感觉较弱
  • 支持离线使用:安装到用户终端,也可以从开始菜单启动的形式

在社内业务应用中,实务上大多会选择 支持离线使用

支持离线使用安装到用户区域注册到开始菜单从本地启动在指定时机确认更新仅在线使用以发布位置为起点启动常驻安装的感觉较弱容易以网络为前提

5. 更新机制

ClickOnce 最容易理解的优势,还是在于 更新模型是内置(built-in)的

更新判定从部署清单开始

ClickOnce 应用会读取部署清单,确认:

  • 是否存在新版本
  • 是否为必需更新
  • 从哪里获取

在此基础上一旦开始更新,ClickOnce 会通过 file patching 来避免冗余的重复下载。从直观上理解,可以认为它是 将新版的应用程序清单与当前版本进行比对,只去获取发生变化的文件,这样在实务中会比较容易理解。

关于更新确认的思路,整理成以下 3 种模式会比较容易理解。

  • 启动前确认
  • 启动后确认
  • 由应用自行提供”检查更新”的 UI

不过,.NET Framework 与 .NET 5+ 在可用的 API 与设置 UI 上存在差异。重要的是 不要仅凭对旧版 ClickOnce 文章的记忆就直接开始实现

更新流程

应用启动确认部署清单是否存在新版本直接启动获取新版的应用程序清单比较文件签名 / 哈希获取发生变化的部分构建新版本并切换必要时重启后以新版本运行

此外,如果没有网络连接,则不会进行更新确认,而是直接运行现有版本,这个前提也提前掌握好,实务中会更不容易混乱。

版本会分开保存

ClickOnce 与”直接就地覆盖当前文件”的思路不同,更接近于 先把新版本完整构建好,再进行切换

此外,ClickOnce 缓存会将 当前版本与前一个版本 分开保存。这是它不容易污染环境、也容易避免版本冲突的原因之一。

用户 B 的 ClickOnce 缓存前一版本当前版本设置 / 数据用户 A 的 ClickOnce 缓存前一版本当前版本设置 / 数据

这里的重点有两个。

  • 不容易与其他用户混在一起
  • 不容易与其他版本冲突

所谓的 DLL Hell 之所以容易避免,这种结构起了很大作用。

6. ClickOnce 的优势

ClickOnce 的好处并不只是”发布简单”。在实务中真正发挥作用的,大致是以下这些方面。

6.1 容易发布给标准用户

ClickOnce 与 per-user 前提的发布方式相性良好,最大的优点是 可以在不需要管理员权限的情况下轻松安装

在社内业务应用中,常见的情况是:

  • 使用者是标准用户
  • 不想每次都向 IT 部门申请安装
  • 但又不想停止更新

在这种条件下,ClickOnce 相当强大。”每个用户的区域中,安全地、连同更新一起安装进去”这个前提,从一开始就与 ClickOnce 契合。

6.2 不需要自行实现更新

如果从零开始构建自动更新,要做的事情会比看起来多得多。

  • 新版本检测
  • 下载
  • 完整性确认
  • 与旧版本的切换
  • 失败时的恢复
  • 更新 UI
  • updater 本身的处理

这些工作中的相当一部分,ClickOnce 已经通过既有模型具备了。

可以交给 ClickOnce 的职责新版本检测获取与验证切换自制 updater 需要承担的职责新版本检测下载完整性确认切换失败时恢复

当然,并不是什么都能自由定制。不过,社内应用所需要的”足够好的更新”,用它相当容易满足。

6.3 应用之间不容易冲突

ClickOnce 应用会 按应用、用户和版本分别隔离

因此,以往常见的

  • 共享组件的版本冲突
  • 覆盖某个 DLL 导致其他应用损坏
  • 手动替换导致环境被污染

这类事故会更不容易发生。

6.4 容易从 Visual Studio 发布

ClickOnce 与 Visual Studio 的发布功能相性良好,距离实际发布的路径很短,这也是一个优点。

在进入 MSI authoring 那样的另一层复杂度之前,就可以先

  • 先发布
  • 先分发
  • 先让更新运转起来
  • 先获得现场反馈

这样的流程很容易搭建起来。

6.5 设置迁移也相对直接

如果使用默认的应用程序设置提供程序,ClickOnce 在更新时具备 将前一版本的设置合并到新版本 的机制。

不过,这是 以默认设置提供程序为前提 的。如果涉及自定义设置保存、自定义提供程序,或更改了保存位置,那自然就不能照搬这个结论了。

7. 适用场景

ClickOnce 特别适用的,大致是以下这类项目。

情况 与 ClickOnce 相性良好的原因
社内使用的 WinForms / WPF 业务应用 标准用户发布与自动更新正好契合
只需按用户单位安装即可 per-user 发布很自然
想通过网站或 UNC 共享发布 发布渠道简单
更新频率大约在每月到每周之间 built-in 更新足以顺利运转
不想把更新 UI 作为产品价值来精心打造 可以直接使用现成的更新模型

举一些实务中相性良好的具体例子,大致是这些。

  • 社内业务录入应用
  • 报价、订单、库存等桌面业务工具
  • 设备配置用的辅助应用
  • 发布给营业所、工厂、后台部门的社内专用客户端
  • 需要更新,但还不到要专门做一个 updater 的应用

在这类应用中,让发布与更新保持简单,本身就是一种价值。而 ClickOnce 正好能直接满足这种价值。

是否适用的判断树

整理需求是否为 WinForms / WPF 等.NET 桌面应用per-user 发布是否足够是否想安装给标准用户能否通过 Web / 文件共享发布是否不需要过度打造更新 UXClickOnce 相当有力比较其他方案

8. 不适用场景

另一方面,不应该勉强使用 ClickOnce 的场景也很明确。

8.1 需要 machine-wide install

ClickOnce 基本上是 per-user 的。

  • 想为所有用户统一安装
  • 想以 Program Files 为前提安装
  • 想对整个终端进行安装与管理

如果是这样,用 MSI 之类的方案会比 ClickOnce 更自然。

8.2 Windows service / driver / shell extension / 深度 COM 注册

这些属于 深度触及 OS 的话题。

  • Windows service
  • driver
  • in-process shell extension
  • 以机械式 COM 注册为前提的构成

一旦涉及到这些,就脱离了 ClickOnce”轻量发布”的世界观。

8.3 需要 package identity

选择 MSIX 的理由之一,就是想获得 package identity 这一需求。

ClickOnce 并不是朝这个方向设计的。如果需要的是 modern packaging 或以 Windows 的 package identity 为前提的功能,应该优先考虑 MSIX。

8.4 想把更新 UX 与分发渠道当作产品来掌控

例如:

  • stable / beta / preview 渠道
  • 分阶段发布
  • 根据 telemetry 调整 rollout 比例
  • 对后台下载的精细控制
  • 自定义的 rollback 策略
  • updater 本身复杂的生命周期

一旦想要这些,ClickOnce 的内置更新就会显得不够用。

8.5 只需放置即可运行的工具

反过来,也存在更简单就足够的情况。

  • 整个文件夹放上去就能运行
  • 更新用手动替换也可以
  • 用 U 盘传递
  • 处于封闭网络,简单性是第一优先

这种情况下,xcopy 发布方式有时摩擦会更小。

哪些需求该转向其他方案

面向所有用户的安装MSI / 部分 MSIXservice / driver / shell extension / 深度 COMMSI / 专用 installer需要 package identityMSIX分阶段发布 / 渠道 / 自定义 UX自制 updater只需放置即可xcopy

ClickOnce 并不是无论哪个方向都万能,但它是一种 在复杂度恰到好处的项目中很强 的方式。

9. 实务中容易踩坑的地方

ClickOnce 很方便,但如果草率导入,会有些坑。以下几点最好提前掌握,会轻松很多。

9.1 不要用”普通 installer”的感觉去看待它

ClickOnce 并不是那种把文件放到固定安装位置、便于人工直接管理的模型。

其实体会进入 ClickOnce 所管理的缓存中,按版本分别隔离。因此,

  • 以固定 EXE 路径为前提的运维方式
  • 手动直接覆盖的运维方式
  • 由人来掌控实体文件位置的运维方式

与这些方式相性都不好。

ClickOnce 是一种 不是由人来管理文件放置位置,而是通过清单来管理发布状态 的方式。

9.2 许多旧的 ClickOnce 文章都以 .NET Framework 为前提

这一点需要特别注意。

即便到现在,搜索结果靠前的位置仍然经常出现 .NET Framework 时代的 ClickOnce 文章。不过,在当前的 .NET 中,情况有些不同。

  • 在 .NET Core 3.1 / .NET 5 / .NET 6 中无法直接使用 ApplicationDeployment API
  • 在 .NET 7 及以后版本中,部分部署属性可以通过环境变量读取
  • 如果要手动处理清单,前提是要用 dotnet-mage.exe
  • Visual Studio 方面,以往基于旧版 Publish Wizard 的说法有时也不能照搬使用

即使自认为”对 ClickOnce 很熟悉”,也不要 凭着旧的记忆直接去实现,这样更安全。

9.3 前提条件要单独考虑

ClickOnce 本身是一种发布模型,但应用要运行起来,有时还需要满足一些前提条件。

  • 所需的运行时
  • 额外的可再发行组件
  • 其他依赖项

这时使用 setup.exe 的 bootstrapper 构成会很有效。反过来,如果这里处理得含糊不清,就会出现”用 ClickOnce 成功发布了,但却运行不起来”的情况。

9.4 设置能否迁移取决于”保存了什么、如何保存”

如果使用默认的设置提供程序,更新时的设置迁移会比较直接。不过,

  • 自定义设置提供程序
  • 以 roaming 为前提
  • 自行更改了设置保存位置
  • 不同版本之间大幅改变了设置结构

如果是这些情况,自然就不能简单照搬了。

9.5 不要轻视签名与更新路径

ClickOnce 具备发布与更新机制,但这并不意味着安全责任就此消失。

尤其是在生产环境中,

  • 如何管理签名证书
  • 如何显示发行者名称
  • 如何管理更新源
  • 如何区分测试用的自签名与正式签名

最好在一开始就整理清楚。

验证失败代码签名证书应用程序清单部署清单客户端进行验证更新 / 运行清单被篡改验证失败并停止

“有自动更新 = 安心”这个等式并不成立,信任什么、以及如何维持这种信任,需要单独考虑。

9.6 如果要迁移部署位置,请重新检查 deploymentProvider

这一点不起眼,但在实务中相当容易踩坑。

已安装的 ClickOnce 应用,会把部署清单中 deploymentProvider 所指向的位置当作更新源去查看。也就是说,即使把整个发布文件夹复制到另一个 URL 或另一个共享位置,如果不更新 deploymentProvider客户端仍可能持续查看原来的位置

而且,一旦手动更改了清单,就需要 重新签名

从发布到更新生效的运维流程

构建生成新的应用程序清单对应用程序清单签名将部署清单更新为新版本对部署清单签名部署到发布位置客户端检测到更新

归根结底,在 ClickOnce 的运维中重要的,与其说是 有没有把文件放上去,不如说是 清单与签名之间的一致性是否得到保证

10. 总结

如果用一句话概括,ClickOnce 就是

一种可以将 .NET 的 Windows 桌面应用 以 per-user 方式轻松发布,并以低成本连更新一起运转的机制

它的优势主要体现在以下几点。

  • 容易发布给标准用户
  • 具备内置的更新模型
  • 容易只更新发生变化的部分
  • 容易将应用与版本分离
  • 容易从 Visual Studio 发布
  • 与社内业务应用相性良好

不过,它并非万能。

  • machine-wide install
  • service / driver / shell extension
  • 深度 COM 注册
  • package identity
  • 自定义渠道或分阶段发布
  • 将更新 UX 作为产品价值精心打造

如果存在这些需求,就应该从 MSI / MSIX / 自制 updater 的方向考虑,而不是 ClickOnce。

ClickOnce 并不是 什么都能装下的简易 installer,但在 适合的项目中,它至今仍相当强大

如果想发布的是

  • .NET 的 Windows 业务应用
  • 按用户单位安装即可满足需求
  • 想发布给标准用户
  • 不想自行维护更新机制

那么 ClickOnce 就是一个相当有力的候选方案。

11. 相关文章

相关主题

与本主题相关的主题页面。可以以本文为起点,进一步访问相关服务或其他文章。

Windows 技术主题

汇总 Windows 开发、故障排查、既有资产利用等技术主题的入口页面。

与本主题相关的服务

本文与以下服务页面相关联,请从相近的入口进入查看。

Windows 应用开发

在社内业务应用、设备配置工具、既有软件改造等项目中,ClickOnce / MSI / MSIX / xcopy 哪种方式摩擦最小,会因实现前的梳理工作而有很大差异。

查看服务 联系我们

技术咨询与设计评审

“ClickOnce 是否足够”“是否应该转向 MSIX 或 MSI”“自动更新应该用 built-in 解决,还是自行维护”,这类判断如果在实现前先整理清楚,会更容易做出决定。

查看服务 联系我们

作者简介

小村 豪

小村软件有限公司 代表

主要从事 Windows 软件开发、技术咨询与故障排查,尤其擅长处理保留既有资产的项目,以及原因难以查明的故障调查。

查看简介 联系我们

12. 参考资料

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

常见问题

汇总了咨询这一主题时常见的问题。

ClickOnce 是什么?
这是一种用于按用户单位轻松发布 .NET Windows 桌面应用,并包含自动更新在内的发布技术。官方说明中将其定义为:一种可通过最少的用户操作完成安装与运行,并具备自我更新能力的 Windows 应用发布技术。实际上,它是以清单(manifest)为核心,统一处理发布、更新与缓存管理的发布模型;应用会保存在每个用户专属的安全缓存区域中,并按应用、用户和版本分别隔离保存。
ClickOnce 今后还能用吗?它不是过时的技术吗?
在当前的 .NET 中,ClickOnce 依然是一个普通的候选方案。Visual Studio 针对 .NET Core 3.1 及 .NET 5 以后的版本使用 Publish 工具,如果需要手动处理清单,则要使用 dotnet-mage.exe。不过需要注意的是,情况与 .NET Framework 时代有所不同:在 .NET Core 3.1 / .NET 5 / .NET 6 中无法直接使用 ApplicationDeployment API,而在 .NET 7 及以后版本中,部分部署属性需要通过环境变量读取。不要仅凭对旧文章的记忆就直接开始实现,这样比较安全。
ClickOnce 适合哪些应用?
特别适合社内使用的 WinForms / WPF 业务应用:想在不需要管理员权限的情况下发布给标准用户、per-user 发布已经足够,并且希望具备内置(built-in)更新能力的场景。发布渠道通过网站或文件共享即可满足。反过来,如果需要面向所有用户的 machine-wide 安装、Windows service、driver、shell extension、深度 COM 注册,或者需要 package identity,又或者想要掌控更新渠道与分阶段发布,就不适合用 ClickOnce,应该从 MSI / MSIX / 自制 updater 的方向来考虑。
ClickOnce 的自动更新机制是怎样的?
更新判定从部署清单(.application)开始。应用会读取部署清单,确认是否存在新版本、是否为必需更新,以及从哪里获取更新。更新时会通过 file patching 机制,将新版的应用程序清单与当前版本进行比对,只获取发生变化的文件,从而避免冗余的重复下载。ClickOnce 缓存会将当前版本与前一个版本分开保存,其思路是先完整构建好新版本,再进行切换。如果没有网络连接,则不会进行更新确认,而是直接运行现有版本。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表