更新紀錄(2 筆,最後更新 2026年09月04日)
本文的修改紀錄。已保存的更新前版本,可透過附有 DOI 的永久連結閱讀。
引用本文(DOI: 10.5281/zenodo.21616340)
本文保存於 Zenodo。以下同時提供一律指向最新版本的 DOI,以及固定於您正在閱讀版本的 DOI。
Go Komura(2026)。〈Windows 應用程式散發方式怎麼選 - MSI/MSIX/ClickOnce/xcopy/自訂更新〉。小村軟體有限公司。https://doi.org/10.5281/zenodo.21616340 https://comcomponent.com/zh-TW/blog/2026/03/20/000-windows-app-deployment-msi-msix-clickonce-xcopy-custom-updater/
- DOI(最新版本)
- 10.5281/zenodo.21616340
- DOI(此版本)
- 10.5281/zenodo.22297181
要決定 Windows 應用程式的散發方式時,話題常常從「哪一個比較新」「哪一個比較簡單」開始。 但實務上真正管用的,是另一組軸線。
- 想以使用者為單位安裝,還是要裝到整台機器
- 更新要交給散發平台,還是自己扛
- 有沒有服務 / 驅動程式 / shell extension / COM 註冊這類 OS 整合
- 需不需要撐得住封閉網路、離線、USB 散發
- 需不需要 package identity,還是想以純 Win32 的身分 unrestricted 地跑
散發方式的選擇,不是 installer 格式的偏好,而是 要動到 OS 多深 與 更新責任由誰扛 的選擇。
flowchart TB
accTitle: 決定散發方式的兩條軸線
accDescr: 說明散發方式的選擇不是哪一個比較新或比較簡單這種 installer 格式的偏好,而是要動到 OS 多深,以及更新責任由誰扛這兩條軸線的選擇的圖。
a0["「哪一個比較新、比較簡單」"] -.-> a1["這條軸線決定不了"]
a2["要動到 OS 多深"] --> a4["散發方式就定下來"]
a3["更新責任由誰扛"] --> a4
圖 1: 散發方式不是靠偏好,而是由 OS 整合的濃度與更新責任這兩條軸線決定。
這篇文章是寫給 正在做 Windows 桌面應用程式,接下來要決定散發方式,或想重新檢視目前方式 的開發者,以及接手後續維運的資訊部門人員。文中不預設特定語言或框架。這個領域有很多術語直接沿用英文流通,所以整理在 1.1。開頭那份工作表的內容摘要放在 1.2。
1. 先講結論
說得粗略一點,但實務上好用的版本是這樣。
- 要裝到整台機器,有服務或 COM 註冊,需要先裝前置相依,就先以 MSI 為起點來想
- 以 Windows 10/11 為前提,想要 clean install / clean uninstall、頻繁更新、package identity,MSIX 很有力
- 想把 .NET 的公司內部桌面應用程式以使用者為單位輕鬆散發並自動更新,ClickOnce 到現在仍然相當強
- 若優先的是 放著就能跑的工具、封閉網路、USB 散發、不需要系統管理員權限,xcopy 最直接了當
- 想連更新 UX、頻道、staged rollout、telemetry、復原策略都自己掌控,那就是 自訂 updater
- 需要驅動程式 時,一開始就別以 MSIX 為中心來想比較安全
- 需要 Explorer 的 in-process shell extension 時,先確認 MSIX 能支援的範圍與 OS 版本條件
粗略歸納起來是這樣。
- 向 OS 註冊的成分很濃 → 偏 MSI
- 想拿到 package identity 與 modern packaging → MSIX
- 想要 per-user 的簡易散發與 built-in 更新 → ClickOnce
- 放著就能用的散發是第一優先 → xcopy
- 有決心自行設計並維運更新平台 → 自訂 updater
flowchart TB
accTitle: 5 種方式的粗略歸納
accDescr: 說明向 OS 註冊的成分很濃就偏 MSI,想拿到 package identity 與 modern packaging 就用 MSIX,想要 per-user 的簡易散發與 built-in 更新就用 ClickOnce,放著就能用的散發是第一優先就用 xcopy,有決心自行設計並維運更新平台就用自訂 updater 的對應關係的圖。
b1{"向 OS 註冊的成分濃不濃"} -->|"濃"| b2["偏 MSI"]
b1 -->|"不濃"| b3{"要不要 package identity"}
b3 -->|"要"| b4["MSIX"]
b3 -->|"不要"| b5{"是不是 per-user 簡易散發+自動更新"}
b5 -->|"是"| b6["ClickOnce"]
b5 -->|"放著就能用最優先"| b7["xcopy"]
b7 -.-> b8["有決心自行掌控更新平台就用自訂 updater"]
圖 2: 猶豫時,依註冊的濃度、identity、散發的簡易度這個順序來釐清。
1.1 本文會出現的術語
散發相關的術語有很多直接沿用英文,先在這裡列出來。
| 術語 | 中文說法 | 意思 |
|---|---|---|
| package identity | 套件識別 | OS 給予已封裝應用程式的唯一識別。有些 Windows 功能沒有它就用不了 |
| authoring | 製作 installer | 定義 MSI 內容的工作。用法接近「撰寫 MSI」 |
| custom action | 自訂動作 | installer 的標準動作不夠用時,用自己的程式碼插入處理的機制 |
| ARP | 應用程式清單 | Add / Remove Programs 的縮寫。指「設定」裡的「已安裝的應用程式」,或控制台「程式和功能」列出的清單 |
| clean install / clean uninstall | 乾淨的安裝 / 移除 | 該裝的都完整裝進去,移除時不留殘骸的狀態 |
| repair | 修復 | 用 installer 的機制,把壞掉的安裝狀態還原回去 |
| telemetry | 使用狀況量測 | 收集更新成敗與使用狀況並加以掌握的機制 |
| per-user / per-machine | 以使用者為單位 / 以電腦為單位 | 只裝給該使用者,還是所有使用者共用一份 |
| shell extension | 檔案總管擴充 | 右鍵選單、圖示顯示等,嵌進檔案總管的元件 |
| unrestricted | 不受限 | 不受套件的限制,以純 Win32 自由存取檔案與登錄檔的狀態 |
| side-by-side | 並存 | 讓多個版本同時放在同一台電腦上 |
| staged rollout | 分階段散發 | 不一次把新版發給所有人,而是定好比例逐步擴大 |
1.2 開頭的工作表裡有什麼
放在開頭的 Excel 工作表,是為了 把本文的判斷套到自己的案子上並記錄下來 而準備的。有日文版與英文版,各由 2 張工作表組成。
Planner 工作表 分成 3 個區塊。
- 輸入表:填完下列 7 個項目,就能把第一候選與理由留成文字
- 散發範圍……per-user / per-machine / 兩者皆有 / 未定
- OS 整合要素……無 / 服務 / 驅動程式 / shell extension / COM 註冊 / 多項
- package identity……需要 / 不需要 / 無法判斷
- 標準使用者安裝需求……必要 / 不需要 / 視條件而定
- 更新頻率……手動或很少 / 每月 / 每週 / 更高
- 目標環境……封閉網路或離線 / 受管理的新版 Windows / 混雜舊世代的 Windows / USB 與現場
- 應用程式類型備註……公司內部 .NET 桌面應用 / 商用產品 / 工具程式 / 混合
- 候選的辨別方式:5 種方式各自的「首先要重點看的條件」與「可以先排除的條件」
- 判斷流程:上面的項目要照什麼順序看,結果又會偏向哪一種方式
Reference 工作表 則是把本文第 3 章的判斷表與第 4 章的比較表原樣搬過來。
換句話說,這就是把文章的第 3、4、7 章做成可以逐案填寫的形式。如果光讀就能得出結論,那不必下載。想比較多個案子、想在公司內留下判斷依據時再用。
flowchart TB
accTitle: 工作表的用法
accDescr: 說明先填完 Planner 工作表的 7 個項目,再用候選的辨別方式與判斷流程靠向某一種方式,最後把第一候選與理由寫下來,把判斷變成逐案記錄的工作表流程的圖。
c1["填完輸入表的 7 個項目"] --> c2["用候選的辨別方式縮小範圍"]
c2 --> c3["用判斷流程靠向某一種方式"]
c3 --> c4["把第一候選與理由寫下來"]
c4 -.-> c5["Reference 工作表是第 3、4 章的表"]
圖 3: 工作表是把文章的判斷,變成逐案記錄的東西。
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 17 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
2. 這 5 種不在同一個擂台上
這一點相當重要。
MSI / MSIX / ClickOnce / xcopy 主要在談 怎麼裝進去。 而 自訂 updater 主要在談 更新責任怎麼扛。
所以在實務上,分成兩層來想會比較好梳理。
| 層 | 主要候選 | 要決定的事 |
|---|---|---|
| 初次安裝 | MSI / MSIX / ClickOnce / xcopy | 放到哪裡、要註冊什麼、權限、解除安裝 |
| 持續更新 | MSIX App Installer / ClickOnce / 手動抽換 / 自訂 updater | 檢查更新、發送來源、簽章驗證、rollback、頻道、UI |
畫成圖是這樣。虛線代表「選了那個安裝方式,就會自然接上的更新手段」。
flowchart TB
APP["想散發的 Windows 應用程式"] --> Q1["首先:怎麼裝進去<br/>初次安裝層"]
APP --> Q2["接著:更新責任由誰扛<br/>持續更新層"]
Q1 --> MSI["MSI"]
Q1 --> MSIX["MSIX"]
Q1 --> CO["ClickOnce"]
Q1 --> XC["xcopy"]
Q2 --> AI["MSIX App Installer"]
Q2 --> COU["ClickOnce 的 built-in 更新"]
Q2 --> MAN["手動抽換"]
Q2 --> OWN["自訂 updater"]
MSIX -.-> AI
CO -.-> COU
MSI -.-> MAN
XC -.-> MAN
MSI -.-> OWN
XC -.-> OWN
圖 4: 初次安裝層與持續更新層的兩層模型。虛線是會自然接上的更新手段。
ClickOnce 與 MSIX 只要決定了上面那一層,下面那一層也差不多定了。相對地,MSI 與 xcopy 必須另外決定下面那一層。「更新要怎麼做」最容易懸在半空中的,就是選了這兩種的時候。
所以把 自訂 updater 想成不是一開始就選的東西,而是既有散發方式滿足不了更新需求時才追加選用的東西,判斷才不會晃動。
flowchart TB
accTitle: 更新會懸空的是 MSI 與 xcopy
accDescr: 說明 ClickOnce 與 MSIX 只要決定安裝方式更新手段也差不多定下來,但 MSI 與 xcopy 必須另外決定持續更新層,而自訂 updater 是在更新需求不足時才追加選用的圖。
d1["選 ClickOnce / MSIX"] --> d2["更新手段也差不多定了"]
d3["選 MSI / xcopy"] --> d4["更新要怎麼做懸在半空"]
d4 --> d5["另外決定持續更新層"]
d5 -.-> d6["不夠時再追加自訂 updater"]
圖 5: 自訂 updater 不是最初的選項,而是補上更新層缺口的追加選擇。
3. 一張表看完的判斷表
先放上最好用的判斷表。
| 情境 | 先選的方式 | 理由 |
|---|---|---|
| 對全體使用者散發,且有服務、COM 註冊、machine-wide 設定 | MSI | 直接站上 Windows Installer 的擂台比較不會出事 |
| 以 Windows 10/11 為前提,想要 clean install / uninstall、頻繁更新、package identity | MSIX | 容易靠向 modern packaging 與 update 模型 |
| 想把 .NET 的公司內部業務應用程式以 per-user 輕鬆散發 | ClickOnce | built-in 的更新模型很好用 |
| 放著就能跑的工具、封閉網路、USB、不需要系統管理員權限 | xcopy | 盡量不把 install 這個概念帶進來 |
| 商用產品,想自己掌控更新 UX 與頻道 | 自訂 updater | 自由度比 built-in 更新高 |
| 需要驅動程式 | 偏向 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 安裝的容易度 | ◎ | ○ | △ | × | ○ |
| built-in 更新 | △ | ◎ | ◎ | × | ◎ |
| package identity | × | ◎ | × | × | × |
| 與服務的契合度 | ◎ | △ | × | × | ○ |
| 與驅動程式的契合度 | △ | × | × | × | ○ |
| 與 shell extension 的契合度 | ◎ | △ | × | × | ○ |
| 封閉網路與離線散發 | ◎ | ○ | ○ | ◎ | ◎ |
| 實作與維運成本 | ○ | ○ | ◎ | ◎ | × |
| 更新 UX 的自由度 | △ | ○ | △ | × | ◎ |
看這張表該問的不是 哪一種最強,而是 哪一種摩擦最小。
5. 各自適合什麼樣的案子
5.1 MSI
MSI 是 想把 Windows 傳統桌面應用程式的 install / uninstall / repair 做到位 時的基準點。
特別適合的是這類案子。
- 對全體使用者散發的業務應用程式
- 含 Windows 服務的應用程式
- 伴隨 COM 註冊、file association、machine-wide 設定的應用程式
- 已經有既有 installer 維運流程的產品
MSI 的強項在於,容易用 Windows 的慣例表達「這個應用程式是怎麼裝進 OS 的」。
另一方面,弱點也很清楚。
- authoring 不起眼卻很難
- upgrade / patch 設計得草率,之後會很難收拾
- custom action 加得越多越容易壞
- 高頻率更新的產品,更新 UX 容易變得笨重
flowchart TB
accTitle: MSI 的強項與代價
accDescr: 說明 MSI 一方面容易用 Windows 的慣例表達應用程式是怎麼裝進 OS 的,另一方面 authoring 很難,custom action 加得越多越容易壞,高頻率更新時更新 UX 也容易變笨重的圖。
e1["MSI"] --> e2["用 Windows 的慣例表達怎麼裝進 OS"]
e2 --> e3["install / uninstall / repair 一應俱全"]
e1 -.-> e4["authoring 不起眼卻很難"]
e4 -.-> e5["custom action 加得越多越容易壞"]
圖 6: MSI 換來 OS 整合的表達力,代價是 authoring 的難度。
5.2 MSIX
MSIX 是 想拿到 modern packaging 與 clean update / uninstall 的選擇。想使用需要 package identity 的 Windows 功能時,意義也很大。
適合的大致是這些。
- 可以把 Windows 10/11 當作前提的桌面應用程式
- 更新頻率偏高的業務應用程式
- 想使用 package identity 才生效的 Windows 功能的應用程式
- 想靠向 Intune 或 App Installer 的案子
MSIX 的強項是 更新與解除安裝很乾淨。
不過,並不是什麼都裝得進去。尤其下面這 4 點,一開始就先確認比較安全。
- in-process shell extension(Windows 11 21H2 之後的 MSIX 可以註冊 legacy context menu handler 等,但需要確認資訊清單的宣告與目標 OS)
- driver
- 以 unrestricted 的舊式 Win32 為前提
- 不想取得 package identity 的架構
這 4 點各自要查的資料不同。不要停在「聽說 MSIX 不行」,先決定好看哪份資料才能拍板,會快很多。
| 想確認的事 | 要看的資料 | 能查到什麼 |
|---|---|---|
| 從哪個 Windows 版本開始可以用 | Microsoft Learn 的「MSIX features and supported platforms」 | 各項功能支援的 OS 版本整理成表 |
| 能不能註冊 in-process shell extension | Microsoft Learn 的「Support legacy context menus for packaged apps」 | 支援的 OS 版本,以及在資訊清單裡的宣告方式 |
| 自己的 installer 能不能轉成 MSIX | Microsoft Learn 的「Prepare to package a desktop application」與「Know your installer」 | 無法封裝的架構清單,以及事前該確認的點 |
| 含服務的架構會變成怎樣 | Microsoft Learn 的「Convert an installer that includes services」 | 含服務的轉換條件與限制 |
這些都在第 9 章的參考資料裡放了連結。判斷時要看到的 不是「有沒有支援」,而是「從哪個版本起、要怎麼宣告才會支援」。這裡沒有連 OS 版本條件一起弄清楚,之後就會以「驗證機上跑得動,現場的舊版 Windows 卻裝不進去」的形式冒出來。
flowchart TB
accTitle: MSIX 的限制要連查證來源一起決定
accDescr: 說明 shell extension 與 driver 等 MSIX 上讓人猶豫的 4 點各自要查的資料不同,因此不要停在聽說不行,而要看到從哪個版本起、要怎麼宣告才會支援,藉此避免驗證機能跑而現場裝不進去的事故的圖。
f1["MSIX 上讓人猶豫的 4 點"] --> f2["不要停在「聽說不行」"]
f2 --> f3["決定用哪份資料拍板"]
f3 --> f4["看到從哪一版起、用什麼宣告才支援"]
f4 -.-> f5["避免驗證機 OK、現場 NG"]
圖 7: MSIX 的限制要查到 OS 版本條件與宣告方式才算拍板。
5.3 ClickOnce
ClickOnce 在 想把 .NET 的公司內部桌面應用程式以 per-user 快速散發,並連更新一起運轉 時,到現在仍然相當強。
適合的是這些場面。
- 公司內部的業務應用程式
- 想以標準使用者身分安裝
- 以使用者為單位散發就夠了
- 不想把更新 UX 做得太細
反過來說,會深入碰 OS 的產品,或是要把多個前置相依綁在一起的 installer 角色,別期待它做到比較安全。
5.4 xcopy
xcopy 是 deploy 而不是 install。沒有登錄檔註冊,沒有修復功能,也沒有 package identity。相對地,只要 放著就能結束,它單純到近乎無敵。
能發揮特色的是這類工具。
- 診斷工具
- 設備設定工具
- 日誌收集工具
- 用 USB 送到現場的工具程式
- 想以 side-by-side 讓多個版本並存的情況
xcopy 的強項是 失敗的方式很好懂。整個資料夾抽換,想退回就退回前一版,這樣的維運方式很好做。
flowchart TB
accTitle: xcopy 是 deploy 而不是 install
accDescr: 說明 xcopy 不具備登錄檔註冊、修復功能與 package identity,換來的是放著就能結束,整個資料夾抽換、想退回就退回前一版,這種失敗方式很好懂的維運的圖。
g1["整個資料夾放上去"] --> g2["就這樣跑起來"]
g2 --> g3["更新就是整個資料夾抽換"]
g3 --> g4["要退回就退回前一版"]
g1 -.-> g5["沒有註冊、修復、identity"]
圖 8: xcopy 的價值在於安裝、更新、退版都很單純。
當然,弱的地方也有。
- Start menu / ARP / repair
- file association / service / shell extension / driver
- built-in 更新
5.5 自訂 updater
自訂 updater 與其說是 自由度的選擇,不如說是 責任的選擇。
值得考慮的,是有這些需求的時候。
- 更新頻率高
- 想要 stable / beta / preview 這類頻道
- 想控制 staged rollout 與推出比例
- 想細緻控制背景下載、通知、維護時段
- 想自己掌握更新 telemetry 與 crash recovery
強項很大,但要付出的也很大。
- 簽章驗證
- 發送用的 manifest
- 重試 / resume
- proxy / firewall / 封閉網路的因應
- rollback
- 壞掉的更新的復原
- updater 本身的更新
也就是說,增加的不是自由度,而是責任。
flowchart TB
accTitle: 自訂 updater 是責任的選擇
accDescr: 說明自訂 updater 換到頻道、staged rollout、telemetry 這類自由度的同時,也要自行設計並維運簽章驗證、發送用 manifest、rollback、壞掉的更新的復原,乃至 updater 本身的更新的責任的圖。
h1["得到的:頻道、staged rollout、telemetry"] --> h3["自訂 updater"]
h2["付出的:簽章驗證、rollback、復原"] --> h3
h3 --> h4["增加的不是自由度,而是責任"]
h2 -.-> h5["updater 本身的更新也是自己的工作"]
圖 9: 自訂 updater 增加的不是自由度,而是更新平台的維運責任。
5.6 決定方式之後,接下來要查什麼
就算決定「走 MSI」,接下來不知道該查什麼,一樣會卡住。這裡列出幾個代表性的入口。它們都不是「就用這個」,而是 選了那個方式的話,先知道名字會比較好的東西。
| 決定的方式 | 接下來要查的東西 | 補充 |
|---|---|---|
| MSI | WiX Toolset | 用 XML 撰寫 MSI 的開放原始碼工具集。是 MSI authoring 的標準入口 |
| MSI | Advanced Installer、InstallShield | 以 GUI 為主的商用 MSI 製作工具。想用 GUI 組出 custom action 與 upgrade 設計時 |
| 不需要 MSI,EXE 格式即可 | Inno Setup、NSIS | 做出來的不是 MSI,而是自有格式的 EXE installer。不會站上 Windows Installer 的擂台 |
| MSIX | MSIX Packaging Tool、makeappx.exe、signtool.exe |
從既有 installer 轉換的工具,以及 Windows SDK 的封裝與簽署工具 |
| MSIX | Windows Application Packaging Project | 在 Visual Studio 這一側把既有專案轉成 MSIX 的專案類型 |
| ClickOnce | Visual Studio 的發行精靈、mage.exe |
發行與產生 manifest。更新的行為由發行時的選項決定 |
| 自訂 updater | Squirrel.Windows、Velopack、WinSparkle、NetSparkle | 會替你準備好更新骨架的函式庫。比起要不要採用,請先用 簽章驗證與 rollback 怎麼處理 來比較 |
這裡要注意的是,選工具和選方式是兩回事。例如 Inno Setup 很好上手,但做出來的不是 MSI,所以以 Windows Installer 為前提的維運,例如用群組原則散發軟體,或用 msiexec 修復,都接不上。如果 5.1 選 MSI 的理由正在那裡,工具也必須配合。
flowchart TB
accTitle: 選工具和選方式是兩回事
accDescr: 說明 Inno Setup 雖然好上手,但做出來的不是 MSI,因此接不上群組原則的軟體散發與 msiexec 的修復,必須依照選擇方式的理由來配合選工具的圖。
i1["決定方式"] --> i2["決定工具"]
i2 -.-> i3["Inno Setup 不會做出 MSI"]
i3 -.-> i4["接不上 GPO 散發與 msiexec 修復"]
i4 -.-> i5["讓工具配合選擇方式的理由"]
圖 10: 好用的工具,不一定站得上你選的那個方式的擂台。
6. 容易猶豫的幾個論點
6.1 是否需要 package identity
如果想要的是以 package identity 為前提的 Windows 功能,MSIX 的價值會一口氣上升。
反過來,若要的是:
- unrestricted 的 file system access
- unrestricted 的 registry access
- elevation / process model 的自由度
- 想原封不動保留舊的 Win32 前提
那麼偏 unpackaged 的方式比較自然。
flowchart TB
accTitle: 以是否需要 package identity 來分
accDescr: 說明想要以 package identity 為前提的 Windows 功能時 MSIX 的價值會一口氣上升,而想要 unrestricted 地存取檔案與登錄檔、想原封不動保留舊的 Win32 前提時,偏 unpackaged 的方式比較自然的圖。
j1{"要不要以 package identity 為前提的功能"} -->|"要"| j2["MSIX 的價值一口氣上升"]
j1 -->|"想 unrestricted 地跑"| j3["偏 unpackaged 的方式比較自然"]
圖 11: 是否需要 identity,是 packaged 與 unpackaged 的分水嶺。
6.2 是否有服務 / 驅動程式 / shell extension
這 3 個會讓散發方式一口氣變重。
- driver:MSIX 不適合
- in-process shell extension:MSIX 不適合
- Windows service:MSI 很自然,MSIX 也能有條件地納入比較
與 OS 綁得越深的要素越多,主題就越不是 看起來簡單的散發,而是 能不能正確地安裝、更新、移除。
6.3 per-user 還是 per-machine
這裡含糊帶過就往前推,之後一定會吵起來。
- 想偏 per-user
- ClickOnce
- xcopy
- 部分的 MSIX
- 想偏 per-machine
- MSI
- 條件符合的話 MSIX
「想在沒有系統管理員權限的情況下安裝」和「想讓所有使用者從同一個位置使用」不是同一件事。
flowchart TB
accTitle: 不要混淆 per-user 與 per-machine
accDescr: 說明想偏 per-user 時候選是 ClickOnce、xcopy 或部分的 MSIX,想偏 per-machine 時候選是 MSI 或條件符合的 MSIX,而想在沒有系統管理員權限下安裝,和想讓所有使用者從同一個位置使用不是同一件事的圖。
k1["想偏 per-user"] --> k2["ClickOnce / xcopy / 部分的 MSIX"]
k3["想偏 per-machine"] --> k4["MSI / 條件符合的話 MSIX"]
k1 -.-> k5["免權限安裝與全員共用位置是兩回事"]
圖 12: 安裝範圍含糊帶過就往前推,之後一定會吵起來。
6.4 更新頻率與維運責任
從更新頻率的感覺來看,大致是這樣。
- 每季到每月更新:MSI 也足以應付
- 每月到每週更新:MSIX / ClickOnce 會輕鬆很多
- 每週到每日更新:開始有理由考慮自訂 updater
- 更新用手動就好 / 由部署那一側控制:xcopy 也夠用
散發方式既是 技術選型,同時也是 維運設計。
flowchart TB
accTitle: 更新頻率的階梯
accDescr: 說明每季到每月更新時 MSI 也足以應付,每月到每週時 MSIX 或 ClickOnce 會輕鬆很多,每週到每日時會出現考慮自訂 updater 的理由,更新用手動就好時 xcopy 也夠用這樣的更新頻率參考值的圖。
m1["每季~每月:MSI 也夠用"] --> m2["每月~每週:MSIX / ClickOnce 輕鬆"]
m2 --> m3["每週~每日:出現考慮自訂 updater 的理由"]
m1 -.-> m4["手動就好的話 xcopy 也夠用"]
圖 13: 更新頻率越高,built-in 更新或自建平台的價值就越大。
6.5 封閉網路與離線散發
在封閉網路裡,單純 往往勝過漂亮的 auto-update。
- xcopy 很強
- MSI 也很強
- ClickOnce 也能走 file share 或 removable media
- MSIX 只要 App Installer 用得對也能運轉
不過在封閉網路裡要頻繁更新,就得連「誰、把新版放到哪、舊版怎麼保留」都決定好,否則光選了方式,維運還是很容易亂掉。
flowchart TB
accTitle: 封閉網路裡單純會勝出
accDescr: 說明封閉網路裡單純往往勝過漂亮的 auto-update,而要頻繁更新時若不連誰、把新版放到哪、舊版怎麼保留都決定好,光選了方式維運還是很容易亂掉的圖。
n1["封閉網路與離線環境"] --> n2["比起漂亮的 auto-update 更要單純"]
n2 --> n3["xcopy 與 MSI 很強"]
n2 -.-> n4["連誰、把新版放到哪都決定好"]
n4 -.-> n5["舊版怎麼保留也要決定"]
圖 14: 封閉網路裡,比選方式更早要決定的是新版與舊版的擺放維運。
7. 猶豫時最後要看的 6 個問題
- 這個應用程式只要 current user 就好,還是必須裝成 machine-wide
- 有沒有 service / driver / 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 放進比較對象
flowchart TB
accTitle: 從 6 個問題得到的落點
accDescr: 說明有服務等 OS 整合就先從 MSI 這一側想,需要 package identity 就優先考慮 MSIX,current user 且以標準使用者安裝的 .NET 桌面應用程式就選 ClickOnce,放著就能維運就選 xcopy,更新頻率高且想掌控更新 UX 就把自訂 updater 放進比較對象的落點的圖。
p1{"有沒有 OS 整合(服務等)"} -->|"有"| p2["先從 MSI 這一側想"]
p1 -->|"沒有"| p3{"需不需要 package identity"}
p3 -->|"需要"| p4["優先考慮 MSIX"]
p3 -->|"不需要"| p5{"是不是 per-user+標準使用者安裝"}
p5 -->|"若是 .NET 桌面應用程式"| p6["ClickOnce 很有力"]
p5 -->|"若是放著就能維運"| p7["xcopy 很有力"]
p6 -.-> p8["想掌控更新 UX 就也把自訂 updater 納入比較"]
圖 15: 照這個順序追這 6 題的答案,落點大致就看得見。
8. 總結
Windows 應用程式的散發方式,大致可以濃縮成下面這一句。
初次安裝要怎麼成立,和 持續更新由誰負責運轉,這兩件事要分開決定。
在此之上,粗略的實務判斷是這樣。
- MSI:深入裝進 OS 的傳統 desktop app
- MSIX:想拿到 package identity 與 modern packaging / update 的 app
- ClickOnce:想把 per-user 的 .NET 業務應用程式輕鬆散發並更新
- xcopy:放著就好、自成一體的工具
- 自訂 updater:有決心自行設計並維運更新本身的產品
而最重要的是這幾點。
- 有 driver / shell extension / service 時,散發方式不是最後的外觀,而是由 OS 整合的方式決定
- 需要 package identity 時,MSIX 的意義就很大
- 自訂 updater 是最後的王牌,不是最初的選項
- 在 封閉網路 裡,單純往往勝過聰明
如果還在猶豫,先把 per-user 還是 per-machine、要向 OS 註冊什麼、更新頻率大概多高 這 3 件事固定下來,討論就能明顯往前推進。
flowchart TB
accTitle: 先固定下來的 3 件事
accDescr: 說明還在猶豫時,先把 per-user 還是 per-machine、要向 OS 註冊什麼、更新頻率大概多高這 3 件事固定下來,散發方式的討論就會往前推進不少的圖。
q1["per-user 還是 per-machine"] --> q4["先固定下來"]
q2["要向 OS 註冊什麼"] --> q4
q3["更新頻率大概多高"] --> q4
q4 --> q5["討論會往前推進不少"]
圖 16: 方式拿不定主意時,至少先把這 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 已保護您的電腦」
從程式碼簽章、EV/OV 憑證、Azure Artifact Signing、MSIX、Microsoft Store、ClickOnce、內部發佈到 App Control,以實務角度整理 Windows 應用程式發佈時出現 SmartScreen 警告的原因。
自動更新功能的安全性基本 - 糟糕的模式與最佳實踐
把自動更新當成信任的發佈而非檔案傳輸來重新整理,從只靠 HTTPS 不夠的理由、signed metadata 與用戶端驗證、簽章金鑰的運營分離、staging 與 fail-closed、rollback 與 freeze 的對策,到 Windows 上 MSIX 與 C...
今日的 Windows 殼層整合 ── 右鍵選單、檔案關聯,以及 Windows 11 的變化
說明 Windows 11 的右鍵選單為何會藏進「顯示更多選項」,並整理副檔名→ProgID→verb 這套關聯基礎、傳統殼層擴充功能的注意事項,以及 IExplorerCommand 與 MSIX/sparse package 的新做法。
AppLocker・App Control for Business(WDAC)與業務應用程式發布 ── 在被「執行控制」封鎖之前
本文整理 AppLocker、App Control for Business(原稱 WDAC)與 Smart App Control 的差異,以及讓自家業務應用程式不被客戶環境的執行控制封鎖,開發與發布方應採取的對策,同時也整理被封鎖時如何解讀事件記錄檔。
如何替換使用中的 exe/DLL ── Restart Manager 與自動更新的「檔案使用中」問題
使用中的 exe/DLL 為什麼無法替換?本文將整理執行中檔案的鎖定與「可以改名」這項特性、Restart Manager 對佔用程序的列舉・終止・重新啟動、rename-then-replace 與 MoveFileEx 的重新開機時置換,並附上 C# 診斷程式碼。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
散發 Windows 應用程式時,必須把服務、驅動程式、WebView2、WinUI 以及企業內部的維運一起納入才能選定方式,實作前先梳理過會很有用。
技術諮詢 & 設計審查
MSI / MSIX / ClickOnce / xcopy 與自訂 updater 不是 installer 的偏好問題,而是更新責任與 OS 整合的設計,因此從需求的釐清開始重新檢視,判斷會容易得多。
常見問題
整理諮詢這個主題時常見的問題。
- MSI 與 MSIX 的差別是什麼?
- MSI 是 Windows 傳統的 installer 格式,與 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 到現在仍然是相當強的選擇。它 built-in 的更新模型很好用,能以標準使用者身分安裝,也不必自己把更新 UX 做出來。另一方面,想使用需要 package identity 的 Windows 功能、重視 clean install / uninstall、想靠向 Intune 或 App Installer 時,MSIX 就很有力。兩者都不適合會深入碰 OS 的產品,所以有服務或驅動程式時要從 MSI 這一側開始想。
- ClickOnce 現在還能用嗎?是不是已經過時的技術?
- 現在還能用,而且在合適的場面上是相當強的選項。想把 .NET 的公司內部桌面應用程式以使用者為單位(per-user)、維持標準使用者權限快速散發,並以 built-in 的更新模型運轉時,它很有效。反過來說,Windows 服務、驅動程式、shell extension 這類會深入碰 OS 的產品,或是要把多個前置相依綁在一起的 installer 角色,最好別期待它做到。以更新頻率來看,每月到每週左右的更新,用 ClickOnce 或 MSIX 都能相當輕鬆地運轉。
- 什麼時候該考慮自訂 updater?
- 自訂 updater 不是一開始就選的東西,而是既有散發方式滿足不了更新需求時,才追加選用的東西。值得考慮的時機是:更新頻率高、想要 stable / beta / preview 這類頻道、想控制 staged rollout 與推出比例、想自己掌握更新 telemetry 與 crash recovery。不過,簽章驗證、發送用的 manifest、重試、rollback、壞掉的更新的復原,乃至 updater 本身的更新,都要由自己設計並扛起維運責任,所以應該把它想成增加的不是自由度,而是責任。