使用ILMerge与.NET 4库

51

两个问题:

1) ILMerge的程序集没有包含基本的.NET程序集

在从.NET 3.5/Visual Studio 2008升级到.NET 4/Visual Studio 2010之后,我在使用ILMerge进行构建后处理时遇到了问题。我有一个解决方案,其中包含几个目标框架设置为".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将会报“不允许未解决的程序集引用:System”错误。如果我不指定MSTest目录的位置,则会出现“不允许未解决的程序集引用:Microsoft.VisualStudio.QualityTools.UnitTestFramework”错误。 上述ILMerge命令可以工作并生成DLL。然而,当我在另一个.NET 4 C#项目中引用该DLL并尝试使用其中的代码时,我会得到以下警告: "The primary reference 'MyILMergedDLL' could not be resolved because it has an indirect dependency on the .NET Framework assembly 'mscorlib, Version=4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' which has a higher version '4.0.65535.65535' than the version '4.0.0.0' in the current target framework." 此时如果我删除 /targetplatform:v4 标志并尝试使用 MyILMergedDLL.dll,我会得到以下错误: "The type 'System.Xml.Serialization.IXmlSerializable' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'." 看起来好像我不应该这么做。使用我的 MyILMergedDLL.dll API 的人不应该必须添加对其引用的任何库的引用。如何解决这个问题? 2) 只有在使用合并的程序集时才会引发TypeLoadException 编辑:即使在使用MyILMergedDLL.dll的消费者项目中添加了对System.Xml的引用,使用MyILMergedDLL.dll中的一些代码也会导致此异常: "System.TypeLoadException: Could not load type 'System.Func`2' from assembly 'MyILMergedDLL, Version=1.0.1.1, Culture=neutral, PublicKeyToken=...'." 这是我消费者项目中的代码;引发TypeLoadException的行是第二个:
var keys = new[] {"a", "b", "c"};
var row = new Row(keys);

抛出 TypeLoadException 异常的特定 Row 构造函数在 MyILMergedDLL 中的一个公共类中定义,当我在引用单个项目 DLL 时使用此构造函数时,它可以正常工作。只有在引用 IL-merged DLL 时使用此构造函数时,才会出现异常。我不知道发生了什么。

以下是该构造函数:

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

它所指的base具有以下代码:

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

虽然这个问题并没有要求提供替代方案,但我想推荐使用Costura Fody。它非常好用。只需要添加一个NuGet引用就可以完成打包 :) - Matthias
6个回答

48

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


补充。您的选项/lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319"有非常严重的问题,特别是在.NET 4.5发布后,它已经让很多程序员陷入麻烦。这个目录不是.NET 4.0参考程序集的正确位置,它的内容已被4.5程序集覆盖,因此您不能再使用它来针对.NET 4.0安装。您得到的运行时错误非常尴尬,程序无法再找到某些类型。通常会在[Extension]属性上出错,有时在ICommand接口上出错。
这些类型和其他一些类型已从一个程序集移动到另一个程序集。使用正确的参考程序集是硬性要求。您必须使用:
 /lib:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"

根据您的特定机器和目标框架版本进行调整。


谢谢提供信息。从那个页面上看不出发布的是哪个版本:VS 2010、C#、.NET 4 还是 ILMerge? - Sarah Vessels
没关系,在迈克·巴尼特的页面上找到了一些信息,他在ILMerge上工作。 :) - Sarah Vessels
27
是的!我没有下载任何新版本,但我仍然将您的回答标记为正确答案(目前唯一的一个...),因为它引导我访问了Mike Barnett的页面,在那里他描述了使用/targetplatform:v4,<path to your v4 framework directory>而不是/targetplatform:v4 /lib:<path to your v4 framework directory> - Sarah Vessels
3
иҝҷжҳҜMikeзҡ„ILMergeдё»йЎөй“ҫжҺҘпјҡhttp://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspxгҖӮ - Jeremy McGee
感谢您的评论-添加了/targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319后,它对我也起作用了。我正在使用.NET 4.0和Visual Studio 2010 SP1。 - Contango
我需要使用/targetplatform:v4,C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0,因为我要将应用程序部署到运行.NET 4.0而不是4.5(我想)的机器上:http://www.mattwrock.com/post/2012/02/29/what-you-should-know-about-running-ilmerge-on-net-45-beta-assemblies-targeting-net-40.aspx - Barrett

23

以下是使用.NET 4.0在Visual Studio 2010 SP1中构建控制台.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 工具复制到解决方案目录中(这样我就可以分发源代码而不必担心记录 ILMerge 的安装)。

高级提示:

如果出现问题无法正常工作,请在 "Post Build" 命令之前添加 "echo"。然后,在 Visual Studio 中打开 "Output" 窗口(View..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

更新

我在“构建后”步骤中添加了以下内容,它将所有的.exe和.dll文件替换为一个单独的组合.exe文件。同时,它也会保留调试用的.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

更新:刚刚在一个有10个自定义子装配件的相当复杂的项目上尝试了这个字符串,它完美地工作了。 - Contango
对于那些将 .dll 合并为输出的人,你必须将 /target:exe 更改为 /target:library,并删除 "$(TargetDir)$(TargetFileName)",否则它会在主 .dll 中加载它,并在通配符 *.dll 中导致 "ILMerge.Merge: ERROR!!: Duplicate type" 错误。 - Despertar
1
请注意,您的帖子有一个非常恶心的错误。 - Hans Passant
3
ILMerge现在是NuGet包。如果您从NuGet包中安装它,它将被放置在“packages”文件夹中,因此我修改了命令行为:"$(SolutionDir)\packages\ilmerge.2.14.1208\tools\ILMerge.exe"。 - Mike Kelly

2
你还可以添加以下配置文件:
<?xml version ="1.0"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <requiredRuntime safemode="true" imageVersion="v4.0.30319" version="v4.0.30319"/>
  </startup>
</configuration>

摘自此处


你把这个文件放在哪里?你给它起什么名字? - brenjt
如果不存在,请创建一个名为ildasm.exe.config的文件,并将我提供的xml放入其中。 - sebagomez

2

SmartAssembly可能相对昂贵,但你的时间价值是多少呢?我花费了比SmartAssembly成本更高的计费小时数,才发现替代方案不起作用且难以调试。这个“昂贵”的产品得到了优质、易于使用和出色支持的支持。 - ATL_DEV

1

只需在Visual Studio属性窗口中选择解决方案资源管理器中的引用,将PresentationCore和PresentationFramework引用设置为“拷贝本地 = True”,即可解决问题,无需硬编码框架路径。我更喜欢这种解决方案,因为路径会因开发人员/构建服务器是64位还是32位而不同,并且随着新的.NET/VS版本的发布而不可避免地会发生变化。


0

针对那些在 .csproj 文件中使用 ILMerge from community tasks 的人:

<ILMerge InputAssemblies="@(MergeAssemblies)"
         ...
         TargetPlatformVersion="v4"
         TargetPlatformDirectory="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"
/>

我们拥有混合的CI构建代理,因此我们使用$(ProgramFiles)环境变量来指向正确的路径(驱动器+x86/x64文件夹),正如MSBuild Team所推荐的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接