在.NET 4库中使用ILMerge

两个问题:

1)不包含在ILMerged程序集中的基本.NET程序集

从.NET 3.5 / Visual Studio 2008升级到.NET 4 / Visual Studio 2010后,我在使用ILMerge进行后构build时遇到了问题。我有一个解决scheme,其中有几个项目的目标框架设置为“.NET Framework 4” 。 我使用下面的ILMerge命令将单个项目DLL合并到单个DLL中:

if not $(ConfigurationName) == Debug if exist "C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe" "C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe" /lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319" /lib:"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies" /keyfile:"$(SolutionDir)$(SolutionName).snk" /targetplatform:v4 /out:"$(SolutionDir)bin\development\$(SolutionName).dll" "$(SolutionDir)Connection\$(OutDir)Connection.dll" ...other project DLLs... /xmldocs 

如果我没有指定.NET 4框架目录的位置,则会从ILMerge中得到“未parsing的程序集引用不允许:系统”错误。 如果我没有指定MSTest目录的位置,我得到“未parsing的程序集引用不允许:Microsoft.VisualStudio.QualityTools.UnitTestFramework”错误。

上面的ILMerge命令工作并产生一个DLL。 但是,当我在另一个.NET 4 C#项目中引用该DLL并尝试使用其中的代码时,出现以下警告:

主要引用“MyILMergedDLL”无法parsing,因为它具有.NET Framework程序集“mscorlib,Version = 4.0,Culture = neutral,PublicKeyToken = b77a5c561934e089”的间接依赖关系,它具有比版本“4.0.65535.65535”更高的版本当前目标框架中的“4.0.0.0”。

如果我然后删除/targetplatform:v4标志并尝试使用MyILMergedDLL.dll,我得到以下错误:

types“System.Xml.Serialization.IXmlSerializable”是在未引用的程序集中定义的。 您必须添加对程序集“System.Xml,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089”的引用。

似乎我不应该这样做。 无论谁使用我的MyILMergedDLL.dll API不应该添加引用它引用的任何库。 我怎样才能解决这个问题?

2)仅当使用合并程序集时TypeLoadException

编辑:除此之外,即使我在使用MyILMergedDLL.dll的使用者项目中添加对System.Xml的引用,使用MyILMergedDLL.dll中的一些代码给出此exception:

System.TypeLoadException:无法从程序集“MyILMergedDLL,Version = 1.0.1.1,Culture = neutral,PublicKeyToken = …”中加载types“System.Func`2”。

这是我的消费者项目中的代码; 导致TypeLoadException的行是第二个:

 var keys = new[] {"a", "b", "c"}; var row = new Row(keys); 

引发TypeLoadException的特定Row构造函数在TypeLoadException中的公共类中MyILMergedDLL ,并且在引用单个项目DLL时使用此构造函数时,它工作正常。 只有当我使用这个构造函数引用IL合并的DLL时,我得到了exception。 我不知道发生了什么事。

这是那个构造函数:

 public Row(IEnumerable<string> keys) : base(keys) { } 

而它所指的base有这样的代码:

 foreach (string key in keys.Where( key => !string.IsNullOrEmpty(key) )) { _dic.Add(key, string.Empty); } 

最近有一个解决x64问题的版本。 如果您仍然有问题,请直接与Mike Barnett联系(microsoft dot com上的mbarnett)


附录。 你的/lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319"选项有一些非常非常错误的/lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319" 。 在.NET 4.5发布之后,这已经让很多程序员陷入困境。 该目录不适用于.NET 4.0引用程序集。 它的内容被4.5程序集覆盖,你不能再用它来定位.NET 4.0的安装。 你得到的运行时错误是非常尴尬的,程序无法find某些types了。 通常在[Extension]属性上轰炸,有时在ICommand界面上轰炸。

这些types和其他一些types从一个程序集移到另一个程序集。 使用正确的参考组件是一项艰巨的任务。 您必须使用:

  /lib:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" 

调整以匹配您的特定机器和目标框架版本。

这里是Visual Studio 2010 SP1的“Post Build String”,使用.NET 4.0。 我正在构build一个控制台.exe,其中包含的所有子.dll文件。

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

基本提示:

  • 注意“\ deploy \”目录:这是输出.exe文件结束的位置。
  • 注意“ILMerge \”目录。 我将ILMerge实用程序复制到我的解决scheme目录中(这样我就可以分发源代码而不必担心loggingILMerge的安装问题)。

高级提示:

如果您在使用时遇到问题,请在“发布后”命令之前添加“回显”。 然后,打开Visual Studio(View..Output)中的“Output”窗口,然后检查Visual Studio实际生成的确切命令。 在我的具体情况下,确切的命令是:

 "T:\PhiEngine\CSharp\ILMerge\ILMerge.exe" /out:"T:\PhiEngine\CSharp\Server Side\deploy\NfServiceDataHod.History.exe" "T:\PhiEngine\CSharp\Server Side\NfServiceDataHod\bin\Debug\NfServiceDataHod.History.exe" "T:\PhiEngine\CSharp\Server Side\NfServiceDataHod\bin\Debug\*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards 

更新

添加到我的“后构build”步骤,它将replace所有的.exe + .dll文件与一个单一的.exe。 它也保持debugging.pdb文件不变:

 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 name we started with. ren "$(TargetDir)$(TargetName).all.pdb" "$(TargetName).pdb" ren "$(TargetDir)$(TargetName).all.exe" "$(TargetName).exe" exit 0 

其他select:

  • 将多个程序集合并为一个WPF应用程序的单个EXE 。
  • .Net反应堆 。
  • SmartAssembly (一个非常昂贵的商业select)。

您还可以添加一个configuration文件与以下内容:

 <?xml version ="1.0"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <requiredRuntime safemode="true" imageVersion="v4.0.30319" version="v4.0.30319"/> </startup> </configuration> 

从这里采取

只需将PresentationCore和PresentationFramework引用设置为在Visual Studio属性窗口中具有“Copy Local = True”(在“解决scheme资源pipe理器”中select引用之后)即可。 它不会对框架path进行硬编码就能解决问题。 我更喜欢这个解决scheme,因为path是不同的,这取决于开发者/构build服务器是64位还是32位,并且不可避免地会随着新的.NET / VS版本的发布而改变。