【问题标题】:using ILMerge with .NET 4 libraries将 ILMerge 与 .NET 4 库一起使用
【发布时间】:2010-06-02 20:29:46
【问题描述】:

两个问题:

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

从 .NET 3.5/Visual Studio 2008 升级到 .NET 4/Visual Studio 2010 后,我在后期构建中使用 ILMerge 时遇到问题。我有一个解决方案,其中包含多个目标框架设置为“.NET”的项目框架 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 收到“不允许的未解析的程序集引用:系统”错误。如果我不指定 MSTest 目录的位置,则会收到“不允许使用未解析的程序集引用:Microsoft.VisualStudio.QualityTools.UnitTestFramework”错误。

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

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

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

“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 中的一些代码也会产生此异常:

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

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

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

引发TypeLoadException 的特定Row 构造函数在MyILMergedDLL 的公共类中定义,当我在引用单个项目DLL 时使用此构造函数时,它工作正常。只有当我在引用 IL 合并的 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 引用一样简单:)

标签: .net dll assemblies c#-4.0 ilmerge


【解决方案1】:

有一个very recent release 来解决 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?
  • 没关系,在 Mike Barnett 的页面上四处挖掘,我看到他在 ILMerge 上工作。 :)
  • 是的!我没有下载任何新版本,但我仍然将你的答案标记为正确答案(目前唯一一个......),因为它让我访问了 Mike Barnett 的页面,他使用/targetplatform:v4,&lt;path to your v4 framework directory&gt; 而不是 @ 987654326@.
  • 这里是 Mike 的 ILMerge 主页的链接:research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx
  • 感谢您的评论 - 添加了 /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 它也对我有用。我正在使用 .NET 4.0 和 Visual Studio 2010 SP1。
【解决方案2】:

这是使用 .NET 4.0 的 Visual Studio 2010 SP1 的“构建后字符串”。我正在构建一个包含所有 sub-.dll 文件的控制台 .exe。

"$(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 (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 个自定义子组件的相当复杂的项目中尝试了这个字符串,并且效果很好。
  • 对于那些以 .dll 作为输出的合并,您必须将 /target:exe 更改为 /target:library 并删除 "$(TargetDir)$(TargetFileName)" 否则它将加载主.dll 和通配符 *.dll 导致“ILMerge.Merge: ERROR!!: Duplicate type”错误。
  • 请注意您的帖子有really nasty bug
  • ILMerge 现在是一个 NuGet 包。如果从那里安装它,它是放在包中的,所以我将命令行修改为:“$(SolutionDir)\packages\ilmerge.2.14.1208\tools\ILMerge.exe”
【解决方案3】:

其他选择:

【讨论】:

  • 相比之下,SmartAssembly 可能比较昂贵,但您的时间值多少钱?我在计费时间上花费的钱远远超过 SmartAssembly 的成本,以发现替代方案不起作用并且无法调试。这种“昂贵”的产品以质量、易用性和出色的支持为后盾。
【解决方案4】:

您还可以添加一个配置文件:

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

取自here

【讨论】:

  • 你把这个文件放在哪里?你给它起什么名字?
  • 如果不存在,您需要使用我在答案中输入的 xml 创建一个ildasm.exe.config 文件
【解决方案5】:

只需在 Visual Studio 属性窗口中将 PresentationCore 和 PresentationFramework 引用设置为“Copy Local = True”(在解决方案资源管理器中选择引用之后)。它将解决问题,而无需对框架路径进行硬编码。我更喜欢这个解决方案,因为路径会根据开发者/构建服务器是 64 位还是 32 位而有所不同,并且随着新 .NET/VS 版本的发布不可避免地会发生变化。

【讨论】:

    【解决方案6】:

    对于那些在 .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 所推荐的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-17
      • 1970-01-01
      • 2011-04-08
      • 2011-06-19
      • 1970-01-01
      • 2017-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多