ILMerge generated assembly doesn't run, although log output reports no errors - why is this?
Asked Answered
F

5

10

I'm testing out ILMerge for a new project, and although the .exe file seems to be created correctly, it won't run.

I have installed ILMerge via the .msi installer (found here http://www.microsoft.com/download/en/confirmation.aspx?id=17630) and am running on a test project using a batch file. Below is the batch file, and the subsequent output log after running. All appears ok in the logs, no errors reported. I am running .NET framework 4.0 for this test project.

When I try to run the .exe, it fails with a standard "This program has stopped working".

I have read that some people have issues running with .NET 4, but I think I've added in the correct arguments to handle this. I get the same result whether I add in the .NET 4 args or not.

Can anyone see why this may be? Thanks in advance.

Batch file

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

Log output:

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.

UPDATE: Here is the dissassembly - looks as I would expect it to

Dissassembly

enter image description here

UPDATE 2

I have found that the component works if I reference from another project and use as an assembly, but not a standalone executable.

Frymire answered 16/11, 2011 at 21:11 Comment(0)
T
10

ILMerge is great if you wrote all of the assemblies that you're trying to merge, and you know that none of them are making assumptions about assembly organization. But under many circumstances (especially ones where heavy reflection or the Dynamic Language Runtime are involved), ILMerge just doesn't work. Sometimes things fail in surprising and mysterious ways.

When ILMerge fails, Jeffrey Richter has a more reliable way to get applications with multiple DLL dependencies to be deployable as a single assembly.

It isn't without trade-offs, but even the ILMerge author, Mike Barnett, said in the comment thread on that blog post "As the author of ILMerge, I think this is fantastic! If I had known about this, I never would have written ILMerge."

If you can use Richter's method, you won't trip over most of the reflection or dynamism traps.

The implementation steps

  1. Embed all of the third-party assemblies that you depend on in your application's Resources.
  2. Register a ResolveEventHandler with the AppDomain.CurrentDomain.AssemblyResolve event.
  3. When your handler gets called with an assembly that you stashed in Resources, load the assembly.

You do part 3 as follows:

var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
return Assembly.Load(new BinaryReader(resourceStream).ReadBytes(int.MaxValue));
Tumbrel answered 22/1, 2012 at 21:34 Comment(4)
Wow thanks. Not tried this but this seems like a tidier way to go about this. Ilmerge is not easy to set up. I will use this method if I come across this again.Frymire
This howto gives more detail than Richters, which I couldn't follow because there was no "Embedded Resource" "Build Action".Tropicalize
Is this solution appropriate also if I just want to embed some of the referenced DLLs?Menell
@Simone, I don't see any reason it wouldn't work fine in that case.Tumbrel
B
5

A fully self-contained executable can be created in Visual Studio 2010 easily, and this method does not require ILMerge at all.

Steps:

  • Create an empty executable project
  • Add your DLLs (and your EXE file) as resources. (Right click on project's name in Solution Explorer, Properties, Resources, Add Resource)

After this Visual Studio will auto-create a new class called Resources, which you have to use in your new project.

  • Add the two methods below to Program.cs to make the final application work.

Then you have to compile your program with Visual Studio and that's it.

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);
}
Braggadocio answered 23/1, 2013 at 17:44 Comment(1)
I put my referencing code in a separate class and reduced this down to just: Assembly start = ...., AppDomain.CurrentDomain.AssemblyResolve += ..., kept the EventHandler code then called the class and it works like a charm. I also edited the EventHandler to specifically call the library I wanted but either way, very easy, much thanks.Moriah
G
1

Perhaps this will not solve your problem, but I think that it is good to verify, that your ilmerged executable doesn't contain references to merged libraries. You can check this with ILSpy (http://wiki.sharpdevelop.net/ILSpy.ashx) - just disassemble your ilmerged executable and check in the tree what assemblies are in references. ILSpy can also help you to verify that your ilmerged executable contains classes from merged assemblies.

Next option could be also to debug assembly bindings when trying to start your ilmerged application (http://msdn.microsoft.com/en-us/library/e74a18c4%28v=VS.100%29.aspx).

You could also try to debug ilmerged assembly to find out if application crashes even before calling main function, or perhaps somewhere inside.

I think that identifying particular type which cannot be found could help to fix the problem.

Goodden answered 16/11, 2011 at 21:34 Comment(1)
There are no merged assemblies included. I have attached the IL Dissassembler windowFrymire
C
0

Check your event viewer for any detailed error messages.

Wrap the body of your void Main(string args[]) method with

try
{
...
}
catch (Exception ex)
{
  Console.WriteLine(ex.ToString());
}

Also try changing your platform target to x86 for example.

You shouldn't need to specify the .net framework directories on the /lib and /targetplatform command line switches if you specify /targetplatform:v4.

Chronoscope answered 17/12, 2011 at 12:42 Comment(0)
K
0

After a few hours of fighting with this issue I deleted my project folder and did a fresh pull from git. I tried cleaned and rebuilt my project and it the same issue. I then compared my coworkers project and inside the *\Library\Build*, I was missing the Results folder. After adding the folder \Library\Build\Results my solution built with no errors.

Korry answered 18/9, 2017 at 20:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.