https://stackoverflow.com/questions/10137937/merge-dll-into-exe https://www.twblogs.net/a/5d6d2a3abd9eee541c33a915 .NET 使用 ILMerge 合併多個程序集,避免引入額外的依賴 我們有多種工具可以將程序集合併成爲一個。打包成一個程序集可以避免分發程序的時候帶上一堆依賴而出問題。 ILMerge 可以用來將多個程序集合併成一個程序集。本文介紹使用 ILMerge 工具和其 NuGet 工具包來合併程序集和其依賴。 本文內容 以 NuGet 包的形式使用 ILMerge 以命令行工具的形式使用 ILMerge 以封裝的 NuGet 包來使用 ILRepack 需要注意 以 NuGet 包的形式使用 ILMerge ILMerge 提供了可供你項目使用的 NuGet 包。如果你在團隊項目當中安裝了 ILMerge 的 NuGet 包,那麼無論團隊其他人是否安裝了 ILMerge 的工具,都可以使用 ILMerge 工具。這可以避免要求團隊所有成員安裝工具或者將工具內置到項目的源代碼管理中。 要以 NuGet 包的形式來使用 ILMerge,需要首先安裝 ILMerge 的 NuGet 包: NuGet Gallery | ilmerge 或者直接在你的項目的 csproj 文件中添加 PackageReference: 我現在有一個項目 Walterlv.Demo.AssemblyLoading,這是一個控制檯程序。這個程序引用了一個 NuGet 包 Ben.Demystifier。爲此帶來了三個額外的依賴。 - Walterlv.Demo.AssemblyLoading.exe - Ben.Demystifier.dll - System.Collections.Immutable.dll - System.Reflection.Metadata.dll 而我們可以使用 ILMerge 將這些依賴和我們生成的主程序合併成一個程序集,這樣分發程序的時候只需要一個程序集即可。 那麼,我們現在需要編輯我們的項目文件: Exe net48 ++ ++ ++ 我們只增加了三行,添加了一個名稱爲 ILMerge 的 Target。(注意到項目文件中我有額外引用一個其他的 NuGet 包 Ben.Demystifier,這是爲了演示將依賴進行合併而添加的 NuGet 包,具體是什麼都沒有關係,我們只是在演示依賴的合併。)在這個 Target 裏面,我們使用 Exec 的 Task 來執行 ILMerge 命令。具體這個命令代表的含義我們在下一節介紹 ILMerge 工具的時候會詳細介紹。如果你希望在你的項目當中進行嘗試,可以把所有 /log 參數之後的那些程序集名稱改爲你自己的名稱。 那麼在編譯的時候使用命令 msbuild /t:ILMerge 就可以完成程序集的合併了。 注意,你普通編譯的話是不會進行 IL 合併的。 如果你希望常規編譯也可以進行 IL 合併,或者說希望在 Visual Studio 裏面點擊生成按鈕的時候也能完成 IL 合併的話,那麼你還需要增加一個跳板的編譯目標 Target。 我將這個名爲 _ProjectRemoveDependencyFiles 的 Target 增加到了下面。它的目的是在 AfterBuild 這個編譯目標完成之後(AfterTargets)執行,然後執行前需要先執行(DependsOnTargets)ILMerge 這個 Target。在這個編譯目標執行的時候還會將原本的三個依賴刪除掉,這樣在生成的目錄下我們將只會看到我們最終期望的程序集 Walterlv.Demo.AssemblyLoading.exe 而沒有其他依賴程序集。 Exe net48 ++ ++ ++ <_ProjectDependencyFile Include="$(OutputPath)Ben.Demystifier.dll" /> ++ <_ProjectDependencyFile Include="$(OutputPath)System.Collections.Immutable.dll" /> ++ <_ProjectDependencyFile Include="$(OutputPath)System.Reflection.Metadata.dll" /> ++ ++ ++ 最終生成的輸出目錄下只有我們最終期望生成的程序集: 最終生成的程序集 以命令行工具的形式使用 ILMerge 你可以在這裏下載到 ILMerge: Download ILMerge from Official Microsoft Download Center 實際上 ILMerge 已經開源,你可以在 GitHub 上找到它: dotnet/ILMerge: ILMerge is a static linker for .NET Assemblies. 裝完之後,如果將 ILMerge 的可執行目錄加入到環境變量,那麼你將可以在任意的目錄下在命令行中直接使用 ILMerge 命令了。加入環境變量的方法我就不用說了,可以在網上搜索到非常多的資料。 ILMerge 裝完的默認目錄在 C:\Program Files (x86)\Microsoft\ILMerge,所以如果你保持默認路徑安裝,那麼幾乎可以直接把這個路徑加入到環境變量中。 那麼 ILMerge 的命令行如何使用呢?它的參數列表是怎樣的呢? 我們來寫一個簡單的例子: ilmerge /ndebug /target:exe /out:Walterlv.Demo.AssemblyLoading.exe /log Walterlv.Demo.AssemblyLoading.exe /log Ben.Demystifier.dll /log System.Collections.Immutable.dll /log System.Reflection.Metadata.dll /targetplatform:v4 其中: /ndebug 表示以非調試版本編譯,如果去掉,將會生成 pdb 文件 /target 合併之後的程序集類型,如果是控制檯程序,則爲 exe /out 輸出文件的名稱(或路徑)(此路徑可以和需要合併的程序集名稱相同,這樣在合併完之後會覆蓋同名稱的那個程序集) /log 所有需要合併的程序集名稱(或路徑) /targetplatform 目標平臺,如果是 .NET Framework 4.0 - .NET Framework 4.8 之間,則都是 v4 在合併完成之後,我們反編譯可以發現程序集中已經包含了依賴程序集中的全部類型了。 合併後的程序集 以封裝的 NuGet 包來使用 ILRepack 安裝 NuGet 包: NuGet Gallery - ILRepack.Lib.MSBuild.Task 之後,你就能直接使用 ILRepack 這個編譯任務了,而不是在 MSBuild 中使用 Exec 來間接執行 ILRepack 的任務。 關於此 NuGet 包的使用,GitHub 中有很棒的例子,可以查看: peters/ILRepack.MSBuild.Task: MSBuild task for ILRepack which is an open-source alternative to ILMerge. 需要注意 如果使用新的基於 Sdk 的項目文件,那麼默認生成的 PDB 是 Portable PDB,但是 ILMerge 暫時不支持 Portable PDB,會在編譯時提示錯誤: An exception occurred during merging: ILMerge.Merge: There were errors reported in dotnetCampus.EasiPlugin.Sample's metadata. 數組維度超過了支持的範圍。 在 ILMerging.ILMerge.Merge() 在 ILMerging.ILMerge.Main(String[] args) 或者英文提示: An exception occurred during merging: ILMerge.Merge: There were errors reported in ReferencedProject's metadata. Array dimensions exceeded supported range. at ILMerging.ILMerge.Merge() at ILMerging.ILMerge.Main(String[] args) 目前,GitHub 上有 issue 在追蹤此問題: Support for portable PDBs · Issue #11 · dotnet/ILMerge ---------- https://blog.darkthread.net/blog/ilmerge-task-on-vs/ Package Source 選 NuGet.tool 後下載 MSBuild.ILMerge.Task 直接編譯最快! 我經常寫小工具程式,不用安裝程式,單一EXE檔隨Copy隨用是最理想的部署設計。不過,程式稍稍複雜就難免依功能屬性拆分多個專案,有時需用到跨專案的共享程式庫,至於引用Json.NET、Dapper、NLog等必備套件的情況更是普遍。例如以下專案,Tool為Console Application(EXE)專案,引用Model類別程式庫專案,還參考了Json.NET: 編譯後會產生三組組件檔:Tool.exe、Model.dll與Newtonsoft.Json.dll,得一起部署到客戶端才能正確執行。 要實現單一EXE檔搞定,.NET有個好用工具-ILMerge,可將多個DLL、EXE檔合併成單一檔案(ILMerge使用方式可參考保哥的介紹文),原本想花點時間研究怎麼安排AfterPost事件執行ILMerge,驚喜地發現已有好心人包成MSBuild的Task! 在NuGet使用msbuild.ilmerge查詢,可以找到MSBuild.ILMerge.Task,二話不說,安裝到專案。 安裝後專案會多出ILMerge.props、ILMergeOrder.txt,但大部分情況下不需修改,直接編譯就好。 重新編譯可發現Model.dll及Newtonsoft.Json.dll不見了,只剩一個變胖的Tool.exe,使用時只需Copy這個檔案就行了。 用JustDecompile解析,可以看到Tool.exe裡藏了Model跟Newtonsoft.Json組件裡的所有型別。 用這種做法即可輕鬆一檔搞定小工具程式的部署,非常方便。 補充:MSBuild.ILMerge.Task預設會將參照到的DLL都包進EXE檔,如果想略過特定DLL,可將DLL的Copy Local屬性設為False即可排除。 ---------- For .NET Framework 4.5 ILMerge.exe /target:winexe /targetplatform:"v4,C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" /out:finish.exe insert1.exe insert2.dll ILMerge https://github.com/dotnet/ILMerge https://github.com/dotnet/ILMerge/blob/master/ilmerge-manual.md 1. Open CMD and cd to your directory. Let's say: cd C:\test 2. Insert the above code. 3. /out:finish.exe replace finish.exe with any filename you want. 4. Behind the /out:finish.exe you have to give the files you want to be combined.