從以前的 .NET Framework 到現在最新的 .NET 框架,所編譯出的結果已經有些不同。以 .NET 框架的 WPF 專案為例,編譯後最少會生成一組 exe 執行檔與對應的 DLL 。
而現代程式在開發時,經常會使用各種套件或自行撰寫的類別庫來加快開發速度,所以使用的越多,編譯出來的執行檔旁邊的 DLL 檔也越多。
而在一些情況下,會將整個編譯輸出的資料夾壓縮後交付給使用者。不過,當檔案數量過多時,使用者在複製或移動時可能會遺漏,甚至誤刪必要的 DLL。若能將專案打包成單一執行檔,這些問題便能避免。
本篇將紀錄在 .NET WPF 專案中產生單一執行檔的可行方案之一,方便日後做參考。
本次的開發環境,如下
- IDE : Visual Studio 2022
- .NET版本 : .NET 8.0
專案輸出現況
首先來回顧一下現有的狀況。
當建立一個新的 .NET WPF 專案並進行編譯後,在 bin 資料夾中,若沒有特別設定,通常會看到與專案名稱相同的 exe,以及一個對應的 DLL。
若專案透過 NuGet 引入了套件,編譯結果中則會多出這些套件所需的 DLL。若該套件依賴多個 DLL,資料夾裡的檔案數量也會隨之增加。
那麼在 .NET 框架下,透過專案內建的機制,搭配一些設定,便可產生單一執行檔。
設定與操作流程
準備好 WPF 專案後,實際的操作並不複雜,只需要依照以下步驟,就能在本地產生單一執行檔。
需要注意的是,這些設定只在第一次進行時才需要,之後會隨著專案一併保存,不需重複設定。
- 在專案上按下右鍵,於選單的上半部點選 「發佈」。
- 在「目標」設定中,因要發佈到本機,選擇 「資料夾」,再點選 「下一步」。
- 在「特定目標」設定中,同樣選擇 「資料夾」,並繼續點選 「下一步」。
- 在「位置」設定中,個人通常使用預設的相對路徑,所以無需額外調整,直接按下 「完成」。
- 系統會自動建立發行設定檔,完成後點選 「關閉」。
- 接著回到發佈畫面,針對要產生的執行檔進行設定,點選下方的 「顯示所有設定」。
- 在設定檔視窗中,預設值如左圖所示:
- 組態 和 目標 Framework 依照專案需求設定。
- 將「目標執行階段」由 可攜式 改為 win-x86,或視需求可改為 win-x64。
- 在下方的「檔案發行選項」中,勾選 「產生單一檔案」。
- 最後點選 「儲存」。
到這裡,相關的設定就完成了。
發佈結果
不論是首次完成設定,或是已經建立好發佈設定檔,之後只需要在專案上按右鍵並選擇 「發佈」,即可進入發佈頁面。
在該頁面中按下 「發佈」 按鈕後,系統會進行編譯並產生輸出檔案。完成後,若想直接開啟輸出位置,只要點選 「巡覽」,即會自動開啟對應的資料夾。
進入 publish 資料夾後,可以看到輸出的結果僅有一個 exe 執行檔。
所有原本的 NuGet 相依 DLL 都已被打包進執行檔中,因此資料夾中沒有額外出現這些檔案。需要注意的是,這項處理僅適用於 .NET 的 DLL。
再來只要使用者的電腦上有安裝相對應版本的 .NET Runtime,即可直接執行該程式。
另外,若有需求,也能在發佈完成後,針對輸出的執行檔進行數位簽章。
補充
部屬模式最後選擇使用 Framework 相依性,是經過一些排列組合測試後個人的選擇。
這邊備忘紀錄一下,選擇部屬模式為獨立式的結果,而其他設定如下圖,
在部署模式的選擇上,最後個人選擇使用 Framework 相依性。
這是經過多個測試不同組合後的結果,因此在這裡也順便做個備忘紀錄。
若改成「獨立式部署」,其設定如下圖:
編譯完成後,到 publish 資料夾中可以看到以下情況:
- 同樣會有專案的執行檔,且仍然沒有 NuGet 相依的 DLL。
- 執行檔的大小,從 Framework 相依性約 10MB,膨脹到 150MB 左右。
- 此外還會額外產生許多 DLL,個人推測這些應該是 C++ 相依的 DLL。
因此,即使有勾選「產生單一檔案」,最終結果還是會因部署模式的不同而產生差異。
自言自語543
進到 .NET 架構之後,甚至在使用 NuGet 已經成為主流 (應該?) 的現在,
整體情況跟當年 .NET Framework 的時代,已經有不少差異。
在想要把程式打包成單一執行檔的過程中,
意外發現這項功能還算方便。
雖然使用者端仍需要安裝對應的 Runtime,
看起來並不是最完美的解法,
但在某些情境下其實已經夠用。
當然,還是會遇到一些限制,
例如套件裡若有使用到 C++ 的 DLL (也就是非 .NET 的 DLL),
就無法透過本文的方法被打包進去。
又或者有人認為,比起單一執行檔,
把所有需要的東西都包進安裝檔,
再透過安裝程序來解決 DLL 散落的問題,
可能才是更好的做法,
若是這樣可以考慮 MSIX。
至於要怎麼把 C++ DLL 包進執行檔,或是進一步使用 MSIX,
那又是另外一段故事了……
未來有機會再另外做分享吧 (?)