Windows 应用发布方式怎么选 - MSI / MSIX / ClickOnce / xcopy / 自定义 updater 判断指南
· 小村 豪 · Windows, 发布, MSI, MSIX, ClickOnce, xcopy, updater
在决定 Windows 应用的发布方式时,人们往往容易从「哪个更新」「哪个更简单」开始讨论。 但在实务中真正起作用的,是另一套判断轴线。
- 是想以使用者单位安装,还是安装到整台机器
- 更新是想交给发布平台负责,还是想自己掌握
- 是否涉及服务 / 驱动程序 / shell extension / COM 注册这类 OS 集成
- 是否需要承受封闭网络、离线、USB 分发的场景
- 是否需要 package identity,还是想以纯 Win32 的方式 unrestricted 运行
发布方式的选择,不是 安装程序格式的偏好,而是 要触及 OS 到什么程度 与 由谁承担更新责任 的选择。
1. 先下结论
说得比较粗略,但在实务上很好用的说法是这样:
- 要安装到整台机器、涉及服务或 COM 注册、需要安装前置组件,那就先以 MSI 为起点考虑
- 以 Windows 10/11 为前提,想要 clean install / clean uninstall、频繁更新、package identity,MSIX 更有力
- 想把 .NET 公司内部桌面应用以使用者单位简单分发并自动更新,ClickOnce 到现在依然相当强
- 优先考虑放着就能跑的工具、封闭网络、USB 分发、无需管理员权限,xcopy 最直接
- 想自己掌握更新 UX、渠道、分阶段发布、telemetry、恢复策略,那就是 自定义 updater
- 需要驱动程序,最好不要一开始就以 MSIX 为中心考虑
- 需要 Explorer 的 in-process shell extension,要先确认 MSIX 能支持的范围和 OS 版本条件
粗略总结起来是这样:
- 对 OS 的注册程度很深 → 偏向 MSI
- 想要 package identity 和 modern packaging → MSIX
- 想要 per-user 的简单分发和内置更新 → ClickOnce
- 放置即可运行的分发最优先 → xcopy
- 已经做好自行设计和运维更新基础设施的准备 → 自定义 updater
2. 这 5 个并不是同一个擂台
这一点相当重要。
MSI / MSIX / ClickOnce / xcopy 主要讨论的是 怎么安装进去。 而 自定义 updater 主要讨论的是 如何承担更新责任。
也就是说,在实务中把它分成两层来考虑会更容易理清思路。
| 层级 | 主要候选 | 要决定的事 |
|---|---|---|
| 首次安装 | MSI / MSIX / ClickOnce / xcopy | 部署到哪里、注册什么、权限、卸载 |
| 持续更新 | MSIX App Installer / ClickOnce / 手动替换 / 自定义 updater | 更新检查、分发源、签名验证、rollback、渠道、UI |
所以,自定义 updater 不是最先选的方案,而是在现有发布方式无法满足更新需求时才追加选择的方案,这样理解不容易跑偏。
3. 一张图看懂的判断表
先放一张最实用的判断表。
| 情况 | 首先选择 | 理由 |
|---|---|---|
| 面向全体用户,涉及服务、COM 注册、machine-wide 设置 | MSI | 老实地站到 Windows Installer 这套体系上,出事的概率更低 |
| 以 Windows 10/11 为前提,想要 clean install / uninstall、频繁更新、package identity | MSIX | 更容易贴合 modern packaging 和更新模型 |
| 想把 .NET 公司内部业务应用以 per-user 方式简单分发 | ClickOnce | 内置的更新模型好用 |
| 放着就能跑的工具、封闭网络、USB、无管理员权限 | xcopy | 尽量不引入「安装」这个概念 |
| 商用产品,想自己掌握更新 UX 和渠道 | 自定义 updater | 比内置更新的自由度更高 |
| 需要驱动程序 | 偏向 MSI 或专用 installer | driver package 是另一个问题,MSIX 不适合 |
| 需要 in-process shell extension | 偏向 MSI 或专用 installer | Windows 11 21H2 及以后可以用 MSIX 注册 legacy context menu handler 等,但需要先确认条件 |
这张表里最重要的一点是:不要仅凭「有更新需求」就直接跳到自定义 updater。
4. 按视角分类的比较
| 视角 | MSI | MSIX | ClickOnce | xcopy | 自定义 updater |
|---|---|---|---|---|---|
| per-user 安装的便利性 | ○ | ○ | ◎ | ◎ | ○ |
| per-machine 安装的便利性 | ◎ | ○ | △ | × | ○ |
| 内置更新 | △ | ◎ | ◎ | × | ◎ |
| package identity | × | ◎ | × | × | × |
| 与服务的适配性 | ◎ | △ | × | × | ○ |
| 与驱动程序的适配性 | △ | × | × | × | ○ |
| 与 shell extension 的适配性 | ◎ | △ | × | × | ○ |
| 封闭网络 / 离线分发 | ◎ | ○ | ○ | ◎ | ◎ |
| 实现与运维成本 | ○ | ○ | ◎ | ◎ | × |
| 更新 UX 的自由度 | △ | ○ | △ | × | ◎ |
这张表要看的重点不是 哪个最强,而是 哪个摩擦最少。
5. 各自适合哪种项目
5.1 MSI
MSI 是想把 Windows 传统桌面应用做到规范的 install / uninstall / repair 时的基准方案。
特别适合的是这类项目:
- 面向全体用户的业务应用
- 包含 Windows 服务的应用
- 涉及 COM 注册、文件关联、machine-wide 设置的应用
- 已经有既有 installer 运维体系的产品
MSI 的优势在于,能用 Windows 的方式清楚表达「应用是如何安装进 OS 的」。
另一方面,它的弱点也很明显:
- authoring 本身其实相当麻烦
- 如果 upgrade / patch 设计得草率,后面很容易受苦
- custom action 越多越容易出问题
- 对高频更新的产品来说,更新 UX 容易变得笨重
5.2 MSIX
MSIX 是想要 modern packaging 以及干净的更新 / 卸载 时的选择。如果想使用依赖 package identity 的 Windows 功能,MSIX 的意义会更大。
适合的场景大致是:
- 可以以 Windows 10/11 为前提的桌面应用
- 更新频率较高的业务应用
- 想使用依赖 package identity 的 Windows 功能的应用
- 想向 Intune 或 App Installer 靠拢的项目
MSIX 的优势在于 更新和卸载的干净程度。
不过,它并不是万能的。以下 4 点最好一开始就先确认:
- in-process shell extension(Windows 11 21H2 及以后的 MSIX 可以注册 legacy context menu handler 等,但需要在 manifest 中声明并确认目标 OS)
- 驱动程序
- unrestricted 的旧版 Win32 前提
- 不想使用 package identity 的架构
5.3 ClickOnce
ClickOnce 在想把 .NET 公司内部桌面应用以 per-user 方式,快速地连同更新一起运转 的场景下,至今仍然相当强。
适合的场景是这样:
- 面向公司内部的业务应用
- 想以标准用户权限安装
- 以使用者单位分发就足够
- 不想在更新 UX 上投入太多精力
反过来,对于需要深度接触 OS 的产品,或者需要像 installer 那样打包多个前置组件的角色,不要对 ClickOnce 抱太大期待,这样更安全。
5.4 xcopy
xcopy 是 deploy 而不是 install。它没有注册表注册,没有修复功能,也没有 package identity。但只要 放着就能用,它就是最强等级的简单方案。
能发挥优势的是这类工具:
- 诊断工具
- 设备配置工具
- 日志收集工具
- 拿 USB 带到现场使用的实用工具
- 想要 side-by-side 共存多个版本的场景
xcopy 的优势是 失败的方式很容易理解。整个文件夹替换、想恢复就换回旧版本,这种运维方式很好操作。
当然,它也有明显的弱点:
- Start menu / ARP / repair
- 文件关联 / 服务 / shell extension / 驱动程序
- 内置更新
5.5 自定义 updater
自定义 updater 与其说是 自由度的选择,更应该说是 责任的选择。
值得考虑的场景是有以下需求时:
- 更新频率高
- 想拥有 stable / beta / preview 这样的渠道
- 想控制分阶段发布或 rollout 比例
- 想细致地控制后台下载、通知、维护时间段
- 想自己掌握更新 telemetry 和 crash recovery
优势很大,但要付出的代价也很大:
- 签名验证
- 分发 manifest
- 重试 / resume
- proxy / firewall / 封闭网络支持
- rollback
- 损坏更新的恢复
- updater 自身的更新
也就是说,增加的不是自由度,而是责任。
6. 容易犹豫的问题
6.1 是否需要 package identity
如果需要的是依赖 package identity 的 Windows 功能,MSIX 的价值会一下子提升。
反过来,如果需要的是:
- unrestricted 的文件系统访问
- unrestricted 的注册表访问
- elevation / process model 上的自由度
- 想原样保留旧版 Win32 前提
那么偏向 unpackaged 的方式会更自然。
6.2 是否有服务 / 驱动程序 / shell extension
这 3 项会一下子让发布方式变重:
- 驱动程序:MSIX 不适合
- in-process shell extension:MSIX 不适合
- Windows 服务:MSI 很自然,MSIX 在满足条件时也可以作为比较对象
与 OS 结合越深的要素越多,主题就不再是 表面上简单的分发,而是 能否正确地安装、更新、删除。
6.3 per-user 还是 per-machine
如果在这一点上含糊不清地推进,之后一定会出问题。
- 想偏向 per-user
- ClickOnce
- xcopy
- 部分 MSIX
- 想偏向 per-machine
- MSI
- 条件符合时的 MSIX
「想在没有管理员权限的情况下安装」和「想让所有用户从同一个位置使用」并不是同一件事。
6.4 更新频率与运维责任
从更新频率的感觉来看,大致是这样:
- 季度到月度更新:MSI 也完全够用
- 月度到周度更新:MSIX / ClickOnce 相当轻松
- 周度到日度更新:出现了考虑自定义 updater 的理由
- 更新可以手动进行,或由部署方控制:xcopy 也足够
发布方式,既是 技术选型,同时也是 运维设计。
6.5 封闭网络 / 离线分发
在封闭网络中,简单 往往比漂亮的 auto-update 更占优势。
- xcopy 很强
- MSI 也很强
- ClickOnce 也可以通过文件共享或 removable media 使用
- MSIX 也可以根据 App Installer 的用法运转
不过,如果要在封闭网络中频繁更新,就必须决定「谁、把新版本放到哪里、旧版本如何保留」,否则即使选好了方式,运维也很容易崩掉。
7. 犹豫时最后要看的 6 个问题
- 这个应用只需要 current user 就够了,还是需要 machine-wide 安装
- 是否涉及服务 / 驱动程序 / shell extension / COM 注册
- 是否使用依赖 package identity 的 Windows 功能
- 是否只想以标准用户权限安装
- 更新频率是月度、周度,还是更高
- 目标环境是否为封闭网络,OS 版本是否统一
只要回答完这 6 个问题,大致就能看到落脚点。
- 第 2 项为「是」 → 先从 MSI 方向考虑
- 第 3 项为「是」 → 优先考虑 MSIX
- 第 1 项为 current user、第 4 项为「是」,且是 .NET desktop app → ClickOnce 更有力
- 第 4 项为「是」、第 2 项为「否」,放置即可运维 → xcopy 更有力
- 第 5 项较高,并且想把更新 UX 当作产品价值来掌握 → 把自定义 updater 也纳入比较对象
8. 总结
Windows 应用的发布方式,可以相当程度地浓缩成这一句话:
把 如何完成首次安装 与 由谁负责持续更新 分开决定。
在此基础上,大致的实务判断是这样:
- MSI:深度进入 OS 的传统 desktop app
- MSIX:想要 package identity 与 modern packaging / update 的 app
- ClickOnce:想把 per-user 的 .NET 业务应用简单分发并更新
- xcopy:放着就能用的自包含工具
- 自定义 updater:已经做好自行设计和运维更新本身的产品
而最重要的是这一点:
- 如果涉及 驱动程序 / shell extension / 服务,发布方式不是由最后的外观决定,而是由 OS 集成的方式决定
- 如果需要 package identity,MSIX 的意义就很大
- 自定义 updater 是最后的王牌,而不是最先的选项
- 在 封闭网络 中,简单往往比精巧更占优势
如果还在犹豫,先把 是 per-user 还是 per-machine、要向 OS 注册什么、更新频率大概是多少 这 3 点先固定下来,事情就能往前推进很多。
9. 参考资料
- Microsoft Learn, Windows Installer
- Microsoft Learn, What is MSIX?
- Microsoft Learn, Packaging overview for Windows apps
- Microsoft Learn, MSIX features and supported platforms
- Microsoft Learn, Support legacy context menus for packaged apps
- Microsoft Learn, App Installer file overview
- Microsoft Learn, Prepare to package a desktop application
- Microsoft Learn, Know your installer
- Microsoft Learn, Convert an installer that includes services
- Microsoft Learn, ClickOnce deployment and security
- Microsoft Learn, Manage updates for a ClickOnce application
- Microsoft Learn, Choosing a ClickOnce deployment strategy
- Microsoft Learn, ClickOnce cache overview
- Microsoft Learn, Windows App SDK deployment guide for self-contained apps
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
Windows出现“Windows 已保护你的电脑”提示的原因
整理Windows应用分发时出现SmartScreen警告的原因,从代码签名、EV/OV证书、Azure Artifact Signing、MSIX、Microsoft Store、ClickOnce、企业内部分发到App Control,以实务角度梳理应对方法。
ClickOnce 是什么 - 从实务角度整理其原理、更新机制以及适用与不适用的场景
本文围绕用于 .NET Windows 桌面应用发布的 ClickOnce 技术,结合 Mermaid 图整理其清单(manifest)、更新、缓存、签名机制,以及适用与不适用的项目类型。
自动更新的安全设计——为什么仅靠 HTTPS 还不够
把自动更新当作信任边界来处理,从实务角度整理带签名的 metadata、客户端验证、密钥分离、防回滚措施与 fail-closed 策略。
委托开发 Windows 应用程序前该梳理的事项
在委托外包开发 Windows 应用程序之前,梳理现有软件改造、设备联动、COM/ActiveX、发布与更新、维护等需要注意的要点。
Windows 什么时候需要管理员权限 - UAC、保护区域与设计上的辨别方式
从边界与存储位置的角度,整理 Windows 什么时候真正需要管理员权限:UAC、保护区域、HKLM、服务、驱动、防火墙。同时说明 per-user 与 per-machine 的差异,以及把管理员处理拆成独立 EXE、服务或任务的设计取舍,帮读者判断该不该提升权限。
常见问题
汇总了咨询这一主题时常见的问题。
- MSI 和 MSIX 有什么区别?
- MSI 是 Windows 传统的安装程序格式,适合 per-machine 部署、Windows 服务、COM 注册、shell extension 这类需要深度接触 OS 的安装场景,能够用 Windows 的方式表达 install / uninstall / repair。MSIX 是以 Windows 10/11 为前提的 modern packaging,优势在于 clean install / clean uninstall、频繁更新和 package identity。但另一方面,MSIX 不适合驱动程序,in-process shell extension 只有在 Windows 11 21H2 及以后才有条件支持,也不适合 unrestricted 的旧版 Win32 前提。如果需要深度注册到 OS,就以 MSI 为起点;如果想要 package identity 和更干净的更新体验,则以 MSIX 为起点。
- MSIX 和 ClickOnce 应该怎么选?
- 如果想把 per-user 的 .NET 公司内部业务应用简单地分发出去并实现自动更新,ClickOnce 至今仍是相当有力的选择。它内置的更新模型好用,可以在标准用户权限下安装,也不需要额外打造更新 UX。另一方面,如果需要使用依赖 package identity 的 Windows 功能、重视 clean install / uninstall,或者想向 Intune、App Installer 靠拢,那么 MSIX 更合适。这两者都不适合需要深度接触 OS 的产品,如果涉及服务或驱动程序,就应该从 MSI 的方向考虑。
- ClickOnce 现在还能用吗?不是过时的技术吗?
- 现在依然可以使用,而且在适合的场景中是相当强的选择。适用场景是:想把面向公司内部的 .NET 桌面应用,以使用者单位(per-user)、在标准用户权限下快速分发,并用内置的更新模型来运转。反过来,对于 Windows 服务、驱动程序、shell extension 这类需要深度接触 OS 的产品,或者需要像 installer 那样打包多个前置组件的角色,就不应该期待 ClickOnce 能胜任。从更新频率的角度看,月度到周度左右的更新节奏,用 ClickOnce 或 MSIX 都能相当轻松地运转。
- 什么时候应该考虑自定义 updater?
- 自定义 updater 不是最先该选的方案,而是在现有发布方式无法满足更新需求时才追加考虑的选项。值得考虑的场景是:更新频率高、想要拥有 stable / beta / preview 这样的渠道、想控制分阶段发布或 rollout 比例、想自行掌握更新 telemetry 和 crash recovery。不过,这也意味着签名验证、发布 manifest、重试、rollback、损坏更新的恢复,以及 updater 自身的更新,都需要自己设计和运维,因此应该把它看作是责任增加而不是自由度增加的选择。
作者简介
本文作者的个人简介页面。
Go Komura
小村软件有限公司 代表
以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。