Windows 应用发布方式怎么选 - MSI / MSIX / ClickOnce / xcopy / 自定义 updater 判断指南

· · Windows, 发布, MSI, MSIX, ClickOnce, xcopy, updater

下载附带日英双语的 Excel 判断工作表

在决定 Windows 应用的发布方式时,人们往往容易从「哪个更新」「哪个更简单」开始讨论。 但在实务中真正起作用的,是另一套判断轴线。

  • 是想以使用者单位安装,还是安装到整台机器
  • 更新是想交给发布平台负责,还是想自己掌握
  • 是否涉及服务 / 驱动程序 / shell extension / COM 注册这类 OS 集成
  • 是否需要承受封闭网络、离线、USB 分发的场景
  • 是否需要 package identity,还是想以纯 Win32 的方式 unrestricted 运行

发布方式的选择,不是 安装程序格式的偏好,而是 要触及 OS 到什么程度由谁承担更新责任 的选择。

1. 先下结论

说得比较粗略,但在实务上很好用的说法是这样:

  • 要安装到整台机器、涉及服务或 COM 注册、需要安装前置组件,那就先以 MSI 为起点考虑
  • 以 Windows 10/11 为前提,想要 clean install / clean uninstall、频繁更新、package identityMSIX 更有力
  • 想把 .NET 公司内部桌面应用以使用者单位简单分发并自动更新ClickOnce 到现在依然相当强
  • 优先考虑放着就能跑的工具、封闭网络、USB 分发、无需管理员权限xcopy 最直接
  • 想自己掌握更新 UX、渠道、分阶段发布、telemetry、恢复策略,那就是 自定义 updater
  • 需要驱动程序,最好不要一开始就以 MSIX 为中心考虑
  • 需要 Explorer 的 in-process shell extension,要先确认 MSIX 能支持的范围和 OS 版本条件

粗略总结起来是这样:

  1. 对 OS 的注册程度很深 → 偏向 MSI
  2. 想要 package identity 和 modern packaging → MSIX
  3. 想要 per-user 的简单分发和内置更新 → ClickOnce
  4. 放置即可运行的分发最优先 → xcopy
  5. 已经做好自行设计和运维更新基础设施的准备 → 自定义 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 个问题

  1. 这个应用只需要 current user 就够了,还是需要 machine-wide 安装
  2. 是否涉及服务 / 驱动程序 / shell extension / COM 注册
  3. 是否使用依赖 package identity 的 Windows 功能
  4. 是否只想以标准用户权限安装
  5. 更新频率是月度、周度,还是更高
  6. 目标环境是否为封闭网络,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. 参考资料

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

常见问题

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

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

返回博客列表