ILMerge生成的程序集不会运行,虽然日志输出报告没有错误 - 为什么会这样?

gb2*_*b2d 10 .net ilmerge

我正在测试ILMerge的新项目,虽然.exe文件似乎是正确创建的,但它不会运行.

我已经通过.msi安装程序(在http://www.microsoft.com/download/en/confirmation.aspx?id=17630中找到)安装了ILMerge,并且正在使用批处理文件运行测试项目.下面是批处理文件,以及运行后的后续输出日志.所有日志中都显示正常,未报告错误.我正在为这个测试项目运行.NET framework 4.0.

当我尝试运行.exe时,它失败并带有标准的"此程序已停止工作".

我已经读过有些人在使用.NET 4时遇到了问题,但我认为我已经添加了正确的参数来处理这个问题.无论我是否添加.NET 4 args,我都得到相同的结果.

任何人都可以看到为什么这可能是?提前致谢.

批处理文件

REM Clear directory first

CD C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL

DEL . /s/q

REM Change dir to iLMerge install (installed via msi installer) 
REM Installer Download: http://www.microsoft.com/download/en/confirmation.aspx?id=17630

CD C:\Program Files (x86)\Microsoft\ILMerge\

REM Combine assemblies with logging

ilmerge.exe /lib:"C:\Windows\Microsoft.NET\Framework\v4.0.30319" /lib:"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies" /t:exe /log:C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL\MergeLog.txt /target:winexe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319 /out:C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL\CombinedDLL.exe C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\TestILMerge.exe C:\WORKING\DIR\TestILMerge\TestDLL2\bin\Debug\TestDLL2.dll C:\WORKING\DIR\TestILMerge\TestDLL3\bin\Debug\TestDLL3.dll
Run Code Online (Sandbox Code Playgroud)

日志输出:

ILMerge version 2.11.1103.0
Copyright (C) Microsoft Corporation 2004-2006. All rights reserved.
ILMerge /lib:C:\Windows\Microsoft.NET\Framework\v4.0.30319 /lib:C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies /t:exe /log:C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL\MergeLog.txt /target:winexe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319 /out:C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL\CombinedDLL.exe C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\TestILMerge.exe C:\WORKING\DIR\TestILMerge\TestDLL2\bin\Debug\TestDLL2.dll C:\WORKING\DIR\TestILMerge\TestDLL3\bin\Debug\TestDLL3.dll 
Set platform to 'v4', using directory 'C:\Windows\Microsoft.NET\Framework\v4.0.30319' for mscorlib.dll
Running on Microsoft (R) .NET Framework v2.0.50727
mscorlib.dll version = 2.0.0.0
The list of input assemblies is:
    C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\TestILMerge.exe
    C:\WORKING\DIR\TestILMerge\TestDLL2\bin\Debug\TestDLL2.dll
    C:\WORKING\DIR\TestILMerge\TestDLL3\bin\Debug\TestDLL3.dll
Trying to read assembly from the file 'C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\TestILMerge.exe'.
    Successfully read in assembly.
    There were no errors reported in TestILMerge's metadata.
Trying to read assembly from the file 'C:\WORKING\DIR\TestILMerge\TestDLL2\bin\Debug\TestDLL2.dll'.
    Successfully read in assembly.
    There were no errors reported in TestDLL2's metadata.
Trying to read assembly from the file 'C:\WORKING\DIR\TestILMerge\TestDLL3\bin\Debug\TestDLL3.dll'.
    Successfully read in assembly.
    There were no errors reported in TestDLL3's metadata.
Checking to see that all of the input assemblies have a compatible PeKind.
    TestILMerge.PeKind = ILonly, Requires32bits
    TestDLL2.PeKind = ILonly
    TestDLL3.PeKind = ILonly
All input assemblies have a compatible PeKind value.
Using assembly 'TestILMerge' for assembly-level attributes for the target assembly.
Merging assembly 'TestILMerge' into target assembly.
Merging assembly 'TestDLL2' into target assembly.
Merging assembly 'TestDLL3' into target assembly.
Copying 2 Win32 Resources from assembly 'TestILMerge' into target assembly.
Transferring entry point 'TestILMerge.Program.Main(System.String[])' from assembly 'TestILMerge' to assembly 'CombinedDLL'.
    There were no errors reported in the target assembly's metadata.
ILMerge: Writing target assembly 'C:\WORKING\DIR\TestILMerge\TestILMerge\bin\Debug\CombinedDLL\CombinedDLL.exe'.
Location for referenced assembly 'mscorlib' is 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll'
    There were no errors reported in  mscorlib's metadata.
ILMerge: Done.
Run Code Online (Sandbox Code Playgroud)

更新:这是反汇编 - 看起来像我期望的那样

Dissassembly

在此输入图像描述

更新2

我发现如果我从另一个项目引用并作为程序集使用该组件,但不是独立的可执行文件.

sbl*_*lom 9

ILMerge非常棒,如果您编写了所有要合并的程序集,并且您知道它们都没有对程序集组织做出假设.但在许多情况下(尤其是涉及重度反射或动态语言运行时的情况),ILMerge只是不起作用.有时事情以令人惊讶和神秘的方式失败.

当ILMerge失败时,Jeffrey Richter有一种更可靠的方法来将具有多个DLL依赖关系的应用程序部署为单个程序集.

这不是没有权衡,但即使是ILMerge的作者Mike Barnett也在博客文章评论帖中说:"作为ILMerge的作者,我认为这太棒了!如果我知道这一点,我永远不会写了ILMerge."

如果你可以使用里希特的方法,你就不会绊倒大多数反射或动力陷阱.

实施步骤

  1. 在应用程序的资源中嵌入您依赖的所有第三方程序集.
  2. 注册ResolveEventHandler参加AppDomain.CurrentDomain.AssemblyResolve活动.
  3. 使用在Resources中存储的程序集调用处理程序时,加载程序集.

你做第3部分如下:

var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
return Assembly.Load(new BinaryReader(resourceStream).ReadBytes(int.MaxValue));
Run Code Online (Sandbox Code Playgroud)

  • [这个方法提供了更多细节](http://adamthetech.com/2011/06/embed-dll-files-within-an-exe-c-sharp-winforms/)而不是里希特斯,我无法跟随,因为那里没有"嵌入式资源""构建行动". (2认同)

小智 5

可以在 Visual Studio 2010 中轻松创建完全独立的可执行文件,并且此方法根本不需要 ILMerge。

脚步:

  • 创建一个空的可执行项目
  • 添加您的 DLL(和您的 EXE 文件)作为资源。(在解决方案资源管理器中右键单击项目名称,属性,资源,添加资源)

在此之后,Visual Studio 将自动创建一个名为 Resources 的新类,您必须在新项目中使用它。

  • 将以下两种方法添加到 Program.cs 中,使最终的应用程序工作。

然后你必须用 Visual Studio 编译你的程序,就是这样。

static void Main(string[] args)
{
    // Load your assembly with the entry point from resources:
    Assembly start = Assembly.Load((byte[]) Properties.Resources.NameOfDllOrExeInYourResources);
    Type t = start.GetType("Foo.Bar.Program");

    // Install the resolver event
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

    // Find Program.Main in the loaded assembly
    MethodInfo m = t.GetMethod("Main", BindingFlags.Public | BindingFlags.Static);

    // Launch the application
    m.Invoke(null, new object[] { args });
}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string assemblyName = new AssemblyName(args.Name).Name.Replace('.', '_');

    // Locate and load the contents of the resource 
    byte[] assemblyBytes = (byte[]) Properties.Resources.ResourceManager.GetObject(assemblyName, Properties.Resources.Culture);

    // Return the loaded assembly
    return Assembly.Load(assemblyBytes);
}
Run Code Online (Sandbox Code Playgroud)