关于c#:ILMerge最佳实践

关于c#:ILMerge最佳实践

ILMerge Best Practices

您是否使用ILMerge? 您是否使用ILMerge合并多个程序集以简化dll的部署? 将组件合并在一起后,您是否发现生产中的部署/版本问题?

我正在寻找有关使用ILMerge减少部署冲突的建议,即使可能的话。


我将ILMerge用于几乎所有不同的应用程序。我已经将它集成到发行版本的构建过程中,因此最终得到的是每个应用程序一个exe,没有额外的dll。

您不能将任何具有本机代码的C ++程序集合并。
您也不能ILMerge包含XAML for WPF的程序集(至少我还没有成功)。它在运行时抱怨找不到资源。

我确实为ILMerge编写了一个包装可执行文件,在其中传递了要合并的项目的启动exe名称和输出exe名称,然后它反映了相关程序集,并使用适当的命令行参数调用了ILMerge。现在,当我将新的程序集添加到项目中时,它变得容易得多,无需记住要更新构建脚本。


介绍

这篇文章显示了如何用单个combined .exe替换所有.exe + .dll files。它还可以使调试.pdb文件保持完整。

对于控制台应用

这是使用.NET 4.0的Visual Studio 2010 SP1的基本Post Build String。我正在构建一个包含所有sub.dll文件的控制台.exe。

1
"$(SolutionDir)ILMerge\\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe""$(TargetDir)$(TargetName).exe""$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319 /wildcards

基本提示

  • 输出为文件" AssemblyName.all.exe",该文件将所有子dll合并为一个.exe。
  • 注意ILMerge\\目录。您需要将ILMerge实用程序复制到您的解决方案目录中(这样您就可以分发源代码而不必担心记录ILMerge的安装),或者更改此路径以指向ILMerge.exe所在的位置。

进阶提示

如果遇到问题,请打开Output,然后选择Show output from: Build。检查Visual Studio实际生成的确切命令,并检查错误。

样例构建脚本

该脚本用单个combined .exe替换所有.exe + .dll files。它还可以使调试.pdb文件保持不变。

若要使用,请将其粘贴到C#项目中Build Events选项卡下的Post Build步骤中,并确保将第一行中的路径调整为指向ILMerge.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rem Create a single .exe that combines the root .exe and all subassemblies.
"$(SolutionDir)ILMerge\\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe""$(TargetDir)$(TargetName).exe""$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319 /wildcards
rem Remove all subassemblies.
del *.dll
rem Remove all .pdb files (except the new, combined pdb we just created).
ren"$(TargetDir)$(TargetName).all.pdb""$(TargetName).all.pdb.temp"
del *.pdb
ren"$(TargetDir)$(TargetName).all.pdb.temp""$(TargetName).all.pdb"
rem Delete the original, non-combined .exe.
del"$(TargetDir)$(TargetName).exe"
rem Rename the combined .exe and .pdb to the original project name we started with.
ren"$(TargetDir)$(TargetName).all.pdb""$(TargetName).pdb"
ren"$(TargetDir)$(TargetName).all.exe""$(TargetName).exe"
exit 0


我们在Microsoft应用程序块上使用ILMerge-而不是12个单独的DLL文件,我们只有一个文件可以上载到我们的客户区域,而且文件系统结构更加整洁。

合并文件后,我必须编辑Visual Studio项目列表,删除12个单独的组件,并添加单个文件作为参考,否则它会抱怨找不到特定的程序集。我不太确定这在后期部署中如何工作,值得尝试一下。


我知道这是一个老问题,但是我们不仅使用ILMerge减少了依赖项的数量,而且还内部化了该实用程序使用的"内部"依赖项(例如automapper,restsharp等)。这意味着它们被完全抽象掉了,并且使用合并的实用程序的项目不需要了解它们。这再次减少了项目中所需的引用,并允许它在需要时使用/更新其自己的同一外部库版本。


请注意,对于Windows GUI程序(例如WinForms),您将需要使用/ target:winexe开关。
/ target:exe开关创建一个合并的控制台应用程序。


我们在组合WPF依赖项时遇到了同样的问题.... ILMerge似乎没有处理这些问题。 Costura.Fody为我们完美地工作了,花了大约5分钟才开始……这是一个非常不错的体验。

只需使用Nuget进行安装(在Package Manager控制台中选择正确的默认项目)。它将自身介绍给目标项目,默认设置对我们立即起作用。

它将所有标记为" Copy Local" = true的DLL合并,并生成一个合并的.EXE(与标准输出一起),压缩后的大小很好(远小于总输出大小)。

该许可证是MIT,因此您可以根据需要进行修改/分发。

https://github.com/Fody/Costura/


我们在许多项目上使用ILMerge。例如,Web Service Software Factory会产生类似8个程序集的输出。我们将所有这些DLL合并到一个DLL中,以便服务主机只需要引用一个DLL。

它使生活更轻松一些,但也没什么大不了的。


合并具有相同命名空间中的资源的DLL时,我们会遇到问题。在合并过程中,资源名称空间之一被重命名,因此无法找到资源。也许我们只是在做错事,仍在调查问题。


我刚开始使用ILMerge作为我的CI版本的一部分,将许多细粒度的WCF合同合并到一个库中。它工作得很好,但是新合并的库不能轻松地与其组件库或依赖于这些组件库的其他库共存。

如果在一个新项目中,您既引用了ILMerged库,又引用了依赖于您向ILMerge提供的输入之一的旧式库,则会发现您无法将ILMerged库中的任何类型传递给ILMerged库中的任何方法。旧版库,无需进行某种类型的映射(例如,自动映射器或手动映射)。这是因为一旦编译完所有内容,这些类型就可以使用程序集名称进行有效限定。

名称也会冲突,但是您可以使用extern别名来解决。

我的建议是避免在合并的程序集中包含合并程序集公开的任何公共可用的lib(例如,通过返回类型,方法/构造函数参数,字段,属性,泛型...),除非您确定知道该用户是您合并的程序集不会也不永远不会依赖同一库的独立版本。


最近,我遇到一个问题,即我在程序集中没有安装程序集,而在Umbraco开源CMS中通过反射调用了一些类。

通过反射进行调用的信息来自具有表名,已实现的类和接口的db表。问题是,当dll被il合并时,反射调用将失败,但是如果将dll分开,则一切正常。我认为问题可能类似于longeasy所遇到的问题?


到目前为止,我们才刚刚开始在我们的解决方案中使用ILMerge,这些解决方案已在其他项目中重新分发和使用。一切似乎都正常。我们甚至直接混淆了打包的程序集。

我们正在考虑对MS Enterprise Library程序集进行相同的操作。

我看到的唯一真正的问题是软件包中各个组件的版本控制。


在我看来,#1 ILMerge最佳实践是"不要使用ILMerge"。而是使用SmartAssembly。原因之一是#2 ILMerge最佳实践是在执行ILMerge之后始终运行PEVerify,因为ILMerge不保证它将正确地将程序集合并为有效的可执行文件。

ILMerge的其他缺点:

  • 合并时,它会剥离XML注释(如果我对此很在意,我会使用混淆工具)
  • 它不能正确处理创建相应的.pdb文件

另一个值得关注的工具是Mono.Cecil和Mono.Linker [2]工具。

[2]:http:// www.mono-project.com/Linker


推荐阅读