ClickOnce 是什麼 - 以實務視角整理機制、更新、適合場面・不適合場面
· 小村 豪 · Windows, 發佈, ClickOnce, .NET, Windows 開發
講到發佈 Windows 的 .NET 桌面應用程式,在 MSI 或 MSIX 的陰影下樸素地好幾次出現的名字是 ClickOnce。
但是這裡粗略地,
- 因為是舊技術所以已經不用
- 反之,因為看起來簡單所以什麼都能用 ClickOnce
任一種偏向通常都會失準。
ClickOnce 不是什麼都能做的萬能 installer。 另一方面,想對標準使用者發佈公司內部的 .NET 業務應用程式,並把更新都包含低成本地轉動的場面,現在仍然相當有力。
本文以實務視角、加入較多 Mermaid 圖,整理 ClickOnce 是什麼、怎麼運作、什麼優秀、哪裡會有勉強。內容以 2026 年 4 月時點可確認的 Microsoft Learn 資訊為主要前提。
圖是概念圖。在支援 Mermaid 的 Markdown 環境中會顯示為圖。
1. 先講結論
ClickOnce 一句話說就是 把 .NET 的 Windows 桌面應用程式以使用者單位簡單發佈、連自動更新都包含在內轉動的發佈技術。
適合的例如以下。
- WinForms / WPF 等公司內部用業務應用程式
- 想用標準使用者導入
- per-user 發佈就夠
- 想把更新內建
- 發佈路徑用 Web 網站或檔案分享就夠
相反,以下的話從一開始看別的方式比較安全。
- 面向全使用者的 machine-wide install
- Windows service、driver、in-process shell extension、濃的 COM 註冊
- 需要 package identity
- 想握有更新頻道、階段發佈、獨自 rollback UX
- 作為安裝程式對 OS 深入的職責大
簡言之 ClickOnce 是 簡單發佈和簡單更新強,但對 OS 整合重的案件不適合 的方式。
先以一頁看定位
flowchart TD
A["想發佈 Windows 應用程式"]
B{"有對 OS 深入整合的要件嗎"}
C["從 MSI / MSIX 側思考"]
D{".NET 的 Windows 桌面應用程式<br/>用 per-user 發佈夠嗎"}
E{"想對標準使用者發佈嗎"}
F{"built-in 更新夠嗎"}
G["ClickOnce 有力"]
H["也比較 xcopy / 獨自 updater"]
A --> B
B -- 是 --> C
B -- 否 --> D
D -- 否 --> H
D -- 是 --> E
E -- 否 --> H
E -- 是 --> F
F -- 是 --> G
F -- 否 --> H
2. ClickOnce 是什麼
ClickOnce 是 Microsoft 的 Windows 應用程式發佈技術。 官方上說明為 以最小的使用者操作就能安裝・執行、能自行更新的 Windows 基礎應用程式的發佈技術。
這裡重要的是不要把 ClickOnce 看作單純的「安裝程式的種類」。
實態上 ClickOnce 是一併照料以下的 發佈模型。
- 發佈哪個版本
- 哪些檔案包含在該版本中
- 如何檢測更新
- 從哪裡取得更新
- 在各使用者的安全位置如何保存
- 啟動時如何確認整合性
也就是說,ClickOnce 的本體不是 setup.exe 本身。
以 manifest 為中心,一併處理發佈・更新・快取管理的機制 看比較接近本質。
在現行的 .NET 中,ClickOnce 本身普通地作為候選進入。Visual Studio 中 .NET Core 3.1 和 .NET 5 以後用 Publish tool,手動處理 manifest 的情況用 dotnet-mage.exe。
ClickOnce 照料的職責
flowchart LR
CO["ClickOnce"]
V["發佈哪個版本"]
U["從哪裡取更新"]
S["保存到各使用者安全位置"]
I["確認整合性後啟動"]
CO --> V
CO --> U
CO --> S
CO --> I
3. 構成 ClickOnce 的東西
理解 ClickOnce 的機制時,看以下 4 個相當容易整理。
| 要素 | 角色 |
|---|---|
佈署 manifest (.application) |
表示現在該發佈哪個版本、更新位置、更新方法等 |
應用程式 manifest (*.exe.manifest) |
表示該版本的應用程式本體、依賴檔案、雜湊、進入點等 |
| 應用程式檔案 | exe、dll、config、資料檔案等 |
setup.exe(任意) |
確認・導入前提條件的 bootstrapper。有必要執行環境或依賴物時使用 |
這裡的核心是 2 種 manifest。
- 佈署 manifest 表示「這個應用程式現在的正解是哪個版本」
- 應用程式 manifest 表示「該版本的內容是什麼」
也就是說,ClickOnce 的更新判定先從佈署 manifest 開始,實際要下載什麼則由應用程式 manifest 握有。
4 要素的關係
flowchart LR
Setup["setup.exe<br/>任意<br/>前提條件的確認 / 導入"]
Deploy["佈署 manifest (.application)<br/>發佈哪個版本<br/>更新位置 / 更新條件"]
App["應用程式 manifest (*.exe.manifest)<br/>該版本的內容<br/>檔案清單 / 雜湊 / 進入點"]
Files["應用程式本體<br/>exe / dll / config / data"]
Cache["ClickOnce 快取<br/>per-user / per-application"]
Setup --> Deploy
Deploy --> App
App --> Files
Files --> Cache
setup.exe 不是主角而是輔助
setup.exe 常被注目,但不是 ClickOnce 的主角。
這是 確認・導入前提條件的輔助角色。
例如需要正確的 .NET 執行環境或追加的可轉散發元件時,setup.exe 先準備好,之後再進入 ClickOnce 本體的發佈。
4. 從安裝到啟動的流程
把 ClickOnce 的流程為實務向相當單純化如下。
- 使用者打開 Web 頁面或檔案分享上的
setup.exe或.application - 使用
setup.exe的構成的話,確認前提條件並放入不足的 - ClickOnce 讀取佈署 manifest
- 讀取佈署 manifest 指向的應用程式 manifest
- 取得必要的檔案,佈署到各使用者的 ClickOnce 快取
- 離線使用有的構成的話,登錄到開始選單或應用程式清單
- 之後從 ClickOnce 管理下啟動應用程式
重點是 不是像普通 installer 那樣放到 Program Files 的發想。
ClickOnce 應用程式進入 各使用者的安全快取區域,按應用程式・按使用者分離。這裡是 ClickOnce 相當大的特徵。
從安裝到首次啟動
sequenceDiagram
participant U as 使用者
participant P as setup.exe / .application
participant D as 佈署 manifest
participant A as 應用程式 manifest
participant C as ClickOnce 快取
participant X as 應用程式本體
U->>P: 打開
Note over U,P: 使用 setup.exe 的構成<br/>先進入前提條件確認
P->>D: 取得並讀取
D->>A: 參照對象版本
A->>C: 取得必要檔案・確認整合性
C->>X: 佈署並啟動
純線上和有離線使用
ClickOnce 有大致 2 種呈現方式。
- 純線上: 以公開位置為起點執行的形式。常駐安裝感薄
- 有離線使用: 導入到使用者的終端,也能從開始選單啟動的形式
公司內部業務應用程式中,實務上多選 有離線使用。
flowchart LR
subgraph Online[純線上]
O1["以公開位置為起點啟動"]
O2["常駐安裝感薄"]
O3["容易變成網路前提"]
O1 --> O2 --> O3
end
subgraph Offline[有離線使用]
F1["導入到使用者區域"]
F2["開始選單登錄"]
F3["從本地啟動"]
F4["在指定時機確認更新"]
F1 --> F2 --> F3 --> F4
end
5. 更新的機制
ClickOnce 最易懂的強處果然還是 更新模型 built-in。
更新判定從佈署 manifest 開始
ClickOnce 應用程式讀取佈署 manifest 確認,
- 是否有新版
- 是否為必須更新
- 從哪裡取得
之後更新開始,ClickOnce 用 file patching 避免冗餘的重新下載。感覺上 比較新版的應用程式 manifest 和現行版,只去取變化的檔案,這樣想實務上容易理解。
更新確認的思考方式以下 3 種模式整理比較易懂。
- 啟動前確認
- 啟動後確認
- 在應用程式側準備「更新確認」UI
但是,.NET Framework 和 .NET 5+ 中能用的 API 或設定 UI 有差異。不要只憑舊 ClickOnce 文章的記憶進入實作 很重要。
更新的流程
flowchart TD
Start["應用程式啟動"]
Check["確認佈署 manifest"]
New{"有新版嗎"}
Run["直接啟動"]
Get["取得新版的應用程式 manifest"]
Compare["比較檔案的簽章 / 雜湊"]
Download["取得變化分"]
Switch["讓新版成立並切換"]
Restart["必要時重啟後以新版執行"]
Start --> Check --> New
New -- 否 --> Run
New -- 是 --> Get --> Compare --> Download --> Switch --> Restart
沒有網路連線的情況,不做更新確認直接執行,這個前提也掌握實務上就不易混亂。
版本分離保持
ClickOnce 比起「把現在的檔案當場覆寫」,更接近 讓新版以正確形式成立後切換 的發想。
此外,ClickOnce 快取中 現行版和前版 分離保持。這是不易弄髒環境、易於避免版本衝突的理由之一。
flowchart TB
subgraph UA[使用者 A 的 ClickOnce 快取]
APrev["前版"]
ACur["現行版"]
AData["設定 / 資料"]
end
subgraph UB[使用者 B 的 ClickOnce 快取]
BPrev["前版"]
BCur["現行版"]
BData["設定 / 資料"]
end
APrev --> ACur
AData --> ACur
BPrev --> BCur
BData --> BCur
這裡的重點是 2 個。
- 不易與別的使用者混在一起
- 不易與別的版本衝突
所謂 DLL Hell 易於避免,就是這個結構很大。
6. ClickOnce 優秀的點
ClickOnce 的好處不只「能簡單發佈」。實務上有效的大致以下。
6.1 易於發佈給標準使用者
ClickOnce 與 per-user 前提的發佈相性好,無需系統管理員權限也易於導入 這點很大。
公司內部業務應用程式常見的是,
- 使用者是標準使用者
- 不想每次向 IT 部門請求安裝
- 但不想停止更新
這種狀況。
這個條件下 ClickOnce 相當強。 「往各使用者的區域,安全地,連更新一起進入」的前提從最初就符合。
6.2 不需要自製更新
從零製作自動更新的話,比外觀更有更多要做的事。
- 新版確認
- 下載
- 整合性確認
- 與舊版切換
- 失敗時復原
- 更新 UI
- updater 自身的處理
ClickOnce 以既有模型持有其中相當大部分。
flowchart LR
subgraph Custom[自製 updater 持有的職責]
C1["新版檢測"]
C2["下載"]
C3["整合性確認"]
C4["切換"]
C5["失敗時復原"]
C1 --> C2 --> C3 --> C4 --> C5
end
subgraph Click[可靠 ClickOnce 的職責]
K1["新版檢測"]
K2["取得與驗證"]
K3["切換"]
K1 --> K2 --> K3
end
當然不是什麼都能自由。 但是 公司內部用應用程式需要的「足夠更新」 相當易於滿足。
6.3 應用程式之間不易衝突
ClickOnce 應用程式 按應用程式・按使用者・按版本分離。
所以,舊式的
- 共用元件的版本競爭
- 覆寫某處的 DLL 讓別的應用程式壞
- 手動替換讓環境變髒
這類事故不易發生。
6.4 易於從 Visual Studio 發行
ClickOnce 與 Visual Studio 的發行功能相性好,到發佈的距離短 也是優點。
在進入 MSI authoring 這類其他困難之前,
- 先發行
- 先發佈
- 先轉更新
- 先獲得現場的回饋
這樣的流程易於製作。
6.5 設定沿用也比較自然
使用預設的應用程式設定 provider 的情況,ClickOnce 有在更新時 把前版的設定合併到新版 的機制。
但這裡是 預設設定 provider 前提。進入獨自設定儲存、獨自 provider、儲存位置的變更的話,當然沒那麼簡單。
7. 適合場面
ClickOnce 特別適合的大致是以下案件。
| 狀況 | 與 ClickOnce 相性好的理由 |
|---|---|
| 公司內部用 WinForms / WPF 業務應用程式 | 標準使用者發佈與自動更新契合 |
| 使用者單位導入就夠 | per-user 發佈自然 |
| 想用 Web 網站或 UNC 共享發佈 | 發佈路徑簡單 |
| 更新頻率每月~每週左右 | built-in 更新就能充分轉動 |
| 不想把更新 UI 當產品價值打磨 | 能用既有更新模型 |
舉實務上相當合適的具體例,如下。
- 公司內部的業務輸入應用程式
- 估價・接單・庫存等桌面業務工具
- 裝置設定用的輔助應用程式
- 發到營業所・工廠・後勤的公司內部專用客戶端
- 想要更新但做不到專用 updater 程度的應用程式
這樣的應用程式,把發佈和更新做得簡單本身就是價值。ClickOnce 相當自然地回應那個價值。
是否適合的判斷樹
flowchart TD
S["整理要件"]
Q1{"是 WinForms / WPF 等的<br/>.NET 桌面應用程式嗎"}
Q2{"per-user 發佈夠嗎"}
Q3{"想裝到標準使用者嗎"}
Q4{"能用 Web / 檔案分享發佈嗎"}
Q5{"更新 UX 不用打磨過度嗎"}
G["ClickOnce 相當有力"]
O["比較其他方式"]
S --> Q1
Q1 -- 否 --> O
Q1 -- 是 --> Q2
Q2 -- 否 --> O
Q2 -- 是 --> Q3
Q3 -- 否 --> O
Q3 -- 是 --> Q4
Q4 -- 否 --> O
Q4 -- 是 --> Q5
Q5 -- 是 --> G
Q5 -- 否 --> O
8. 不適合場面
另一方面,不要勉強使用 ClickOnce 的場面也很明確。
8.1 需要 machine-wide install
ClickOnce 基本是 per-user。
- 想全使用者共通安裝
- 想以
Program Files為前提安裝 - 想對整個終端導入・管理
的話不是 ClickOnce 而是 MSI 等比較自然。
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 調整推出率
- 背景下載的細緻控制
- 獨自的 rollback 策略
- updater 自身的複雜生命週期
想要這些,ClickOnce 的 built-in 更新會不夠。
8.5 放著用就夠的工具
相反,也有更單純就好的情況。
- 放整個資料夾就能運作
- 更新也用手動替換就好
- 用 USB 交給
- 在封閉環境單純最優先
的話,xcopy 發佈摩擦較少的情況也有。
什麼要件偏向別方式
flowchart LR
A1["全使用者用導入"] --> B1["MSI / 一部 MSIX"]
A2["service / driver / shell extension / 濃的 COM"] --> B2["MSI / 專用 installer"]
A3["需要 package identity"] --> B3["MSIX"]
A4["階段發佈 / 頻道 / 獨自 UX"] --> B4["獨自 updater"]
A5["放著就夠"] --> B5["xcopy"]
也就是說 ClickOnce 向上也向下都不萬能。 是 對剛好的複雜度案件強 的方式。
9. 實務上容易卡的點
ClickOnce 雖然方便,但粗略進入會稍微卡。特別以下先掌握比較輕鬆。
9.1 不要用與「普通 installer」相同感覺看
ClickOnce 不是以固定的安裝位置讓人直接易於管理的形式放的模型。
實體進入 ClickOnce 管理的快取,按版本分離。 所以,
- 以固定 EXE 路徑為前提的運營
- 手工直接覆寫的運營
- 實體檔案位置由人握的運營
相性不好。
ClickOnce 是 檔案佈署不是由人管理的方式,而是以 manifest 管理發佈狀態的方式。
9.2 舊 ClickOnce 文章多是 .NET Framework 前提
這裡相當重要。
現在搜尋上位仍然很多是 .NET Framework 時代的 ClickOnce 文章。 但是,現在的 .NET 事情稍微不同。
- .NET Core 3.1 / .NET 5 / .NET 6 中
ApplicationDeploymentAPI 不能直接使用 - .NET 7 以後能透過環境變數讀取一部分佈署屬性
- 手動處理 manifest 的話前提是
dotnet-mage.exe - Visual Studio 側也有舊 Publish Wizard 前提的話題直接不通的情況
也就是說 即使「認為懂 ClickOnce」,也不要以舊記憶原樣實作 比較安全。
9.3 前提條件另外考慮
ClickOnce 本身是發佈模型,但應用程式要運作有時需要前提條件。
- 支援的執行環境
- 追加的可轉散發元件
- 其他依賴物
這時使用 setup.exe 的 bootstrapper 構成有效。反之曖昧這裡會發生「ClickOnce 能發佈但不運作」。
9.4 設定的沿用看 “用什麼怎麼儲存”
預設設定 provider 的話更新時的設定遷移比較自然。 但是,
- 獨自設定 provider
- roaming 前提
- 自己改變設定儲存位置
- 以版本差分大幅改變設定結構
的話,當然沒那麼簡單。
9.5 不要輕忽簽章和更新路徑
ClickOnce 擁有發佈和更新的機制,但不代表安全責任消失。
特別在正式環境,
- 如何管理簽章憑證
- 如何顯示發行者名
- 如何管理更新源
- 如何分開測試用自簽和正式簽章
最初整理比較好。
flowchart TD
Cert["程式碼簽章憑證"]
AppMan["應用程式 manifest"]
DepMan["佈署 manifest"]
Verify["在用戶端驗證"]
Run["更新 / 執行"]
Tamper["manifest 竄改"]
Stop["驗證失敗並停止"]
Cert --> AppMan
Cert --> DepMan
AppMan --> Verify
DepMan --> Verify
Verify --> Run
Tamper -. 驗證失敗 .-> Stop
「有自動更新 = 放心」不是,信任什麼、怎麼維持那個信任 需要另外考慮。
9.6 移動佈署處的話重新檢視 deploymentProvider
這很樸素,但實務上相當卡。
已安裝的 ClickOnce 應用程式去看佈署 manifest 內 deploymentProvider 指向的位置當作更新源。
也就是說,就算把公開資料夾整個複製到別 URL 或別分享,不更新 deploymentProvider 的話 客戶端會持續看原位置。
然後,如果手動改 manifest,需要 重新簽章。
從發行到更新反映的運營流程
flowchart TD
Build["建置"]
AppManifest["產生新的應用程式 manifest"]
SignApp["對應用程式 manifest 簽章"]
UpdateDep["把佈署 manifest 更新到新版本"]
SignDep["對佈署 manifest 簽章"]
Publish["佈署到公開位置"]
Client["客戶端檢測更新"]
Build --> AppManifest --> SignApp --> UpdateDep --> SignDep --> Publish --> Client
簡言之,ClickOnce 運營中重要的比起 是否放了檔案,是 manifest 和簽章的整合性是否一致。
10. 總結
ClickOnce 一句話說,
把 .NET 的 Windows 桌面應用程式, 用 per-user 簡單發佈、連更新都低成本轉動的機制
優秀的主要是以下點。
- 易於發佈給標準使用者
- 有 built-in 的更新模型
- 易於只更新變化分
- 易於分離應用程式和版本
- 易於從 Visual Studio 發行
- 與公司內部用業務應用程式相性好
但是不萬能。
- machine-wide install
- service / driver / shell extension
- 濃的 COM 註冊
- package identity
- 獨自頻道或階段發佈
- 把更新 UX 當產品價值打磨
有這類要件的話,應該從 MSI / MSIX / 獨自 updater 側而非 ClickOnce 思考。
也就是 ClickOnce 不是 什麼都能進入的簡易 installer。 代替的是 在適合的案件現在仍然相當強。
如果想發佈的是
- .NET 的 Windows 業務應用程式
- 使用者單位的導入就夠
- 想發佈到標準使用者
- 不想自己持有更新
那麼 ClickOnce 相當有力候選。
11. 相關文章
- Windows 應用程式的發佈方式怎麼選 - MSI / MSIX / ClickOnce / xcopy / 獨自 updater 的判斷表
- 自動更新程式是信任邊界 - 僅靠 HTTPS 不夠的理由
- Windows 的系統管理員特權什麼時候必要 - UAC、保護區域、設計上的辨別法
12. 參考資料
- Microsoft Learn - ClickOnce deployment and security
- Microsoft Learn - ClickOnce for .NET on Windows
- Microsoft Learn - How ClickOnce performs application updates
- Microsoft Learn - Choosing a ClickOnce update strategy
- Microsoft Learn - ClickOnce deployment manifest
- Microsoft Learn - ClickOnce application manifest
- Microsoft Learn - ClickOnce cache overview
- Microsoft Learn - ClickOnce and application settings
- Microsoft Learn - Install prerequisites with a ClickOnce application
- Microsoft Learn - ClickOnce and Authenticode
- Microsoft Learn - Security, versioning, and manifest issues in ClickOnce deployments
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
用 Windows 沙箱加速 Windows 應用程式開發的驗證 - 以實務向整理管理員權限問題、乾淨環境、權限不足・資源不足的重現
整理在 Windows 應用程式開發中如何運用 Windows Sandbox 加速驗證的實務做法。透過按情境分檔的 .wsb、唯讀輸入與專用 Outbox 寫入分離、在容器內另建標準使用者重現權限不足、以及降低記憶體和關閉 vGPU 製造資源不足偏向,把每次的乾淨環境準備...
自動更新功能的安全性基本 - 糟糕的模式與最佳實踐
把自動更新當成信任的發佈而非檔案傳輸來重新整理,從只靠 HTTPS 不夠的理由、signed metadata 與用戶端驗證、簽章金鑰的運營分離、staging 與 fail-closed、rollback 與 freeze 的對策,到 Windows 上 MSIX 與 C...
Windows 的 DLL 名稱解析機制 - 以實務角度整理搜尋順序、Known DLLs、API set、SxS
從實務角度整理 Windows 的 DLL 名稱解析,說明 loader 在掃描檔案系統前會先處理 DLL redirection、API set、SxS、Known DLLs,並用 SetDefaultDllDirectories 與 LoadLibraryEx 旗標縮小...
Windows 什麼時候需要系統管理員權限 - UAC、保護區、設計上的分辨方式
從邊界與儲存位置的角度,整理 Windows 何時真正需要系統管理員權限:UAC、保護區、HKLM、服務、驅動、防火牆。同時說明 per-user 與 per-machine 的差異,以及把管理員處理切成獨立 EXE、服務或工作的設計取捨,幫讀者判斷該不該提權。
Windows 應用安全處理子行程的 checklist - Job Object、結束傳播、標準輸入輸出、watchdog 的最佳實務
在 Windows 應用上安全處理子行程,關鍵不在挑啟動 API,而是設計行程樹的擁有者與結束流程。本文整理 Job Object 的 KILL_ON_JOB_CLOSE、GUI 與 console 的 graceful shutdown、stdio 平行抽乾與 EOF、w...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
Windows 軟體維護 & 現代化
支援既有 Windows 軟體的階段性升級、功能追加、64 位元就緒以及可維護性的重構。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。