using ILMerge with .NET 4 libraries

58,691

Solution 1

There was a very recent release to solve x64 problems. Get in touch with Mike Barnett directly if you still have problems (mbarnett at microsoft dot com)


Addendum. There's something very, very wrong about your /lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319" option. This has been getting lots of programmers in trouble lately, after .NET 4.5 was released. That directory is not the proper one for .NET 4.0 reference assemblies. Its content gets overwritten with the 4.5 assemblies, you can no longer use it to target a .NET 4.0 install. The runtime error you get is very awkward, the program cannot find certain types anymore. Usually bombing on the [Extension] attribute, sometimes on the ICommand interface.

These types, and some others, were moved from one assembly to another. Using the correct reference assemblies is a rock-hard requirement. You must use:

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

Adjust to match your particular machine and target framework version.

Solution 2

Here is the "Post Build String" for Visual Studio 2010 SP1, using .NET 4.0. I am building a console .exe with all of the sub-.dll files included in it.

"$(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

Basic hints:

  • Notice the "\deploy\" directory: this is where the output .exe file ends up.
  • Notice the "ILMerge\" directory. I copied the ILMerge utility into my solution directory (so I could distribute the source without having to worry about documenting the install of ILMerge).

Advanced hints:

If you have problems with it not working, add an "echo" before the "Post Build" command. Then, open the "Output" window in Visual Studio (View..Output), and check the exact command that Visual Studio actually generated. In my particular case, the exact command was:

"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

Update

Added this to my "Post Build" step, it replaces all .exe + .dll files with a single combined .exe. It also keeps the debugging .pdb file intact:

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

Solution 3

Other alternatives:

Solution 4

You can also add a config file with the following:

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

Taken from here

Solution 5

Just set PresentationCore and PresentationFramework references to have "Copy Local = True" in the Visual Studio properties window (after selecting the references in the Solution Explorer). It will solve the problem without hard-coding the framework path. I prefer this solution because the path is different depending on whether the developer/build server is 64bit or 32bit and inevitably will change as new .NET/VS versions are released.

Share:
58,691
Sarah Vessels
Author by

Sarah Vessels

I'm a software developer at GitHub, working out of Nashville, Tennessee. I love black tea and electropop, puns and hot chicken. When I'm not writing code, I'm playing video games like Skyrim, Diablo 3, and The Sims series. I sometimes blog about video games and tech.

Updated on July 09, 2022

Comments

  • Sarah Vessels
    Sarah Vessels almost 2 years

    Two problems:

    1) Basic .NET Assembly Not Included in ILMerged Assembly

    I'm having trouble using ILMerge in my post-build after upgrading from .NET 3.5/Visual Studio 2008 to .NET 4/Visual Studio 2010. I have a Solution with several projects whose target framework is set to ".NET Framework 4". I use the following ILMerge command to merge the individual project DLLs into a single 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 
    

    If I leave off specifying the location of the .NET 4 framework directory, I get an "Unresolved assembly reference not allowed: System" error from ILMerge. If I leave off specifying the location of the MSTest directory, I get an "Unresolved assembly reference not allowed: Microsoft.VisualStudio.QualityTools.UnitTestFramework" error.

    The ILMerge command above works and produces a DLL. When I reference that DLL in another .NET 4 C# project, however, and try to use code within it, I get the following warning:

    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.

    If I then remove the /targetplatform:v4 flag and try to use MyILMergedDLL.dll, I get the following error:

    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'.

    It doesn't seem like I should have to do that. Whoever uses my MyILMergedDLL.dll API should not have to add references to whatever libraries it references. How can I get around this?

    2) TypeLoadException Only When Using Merged Assembly

    Edit: beyond this, even if I do add a reference to System.Xml in the consumer project that uses MyILMergedDLL.dll, making use of some code in MyILMergedDLL.dll gives this exception:

    System.TypeLoadException: Could not load type 'System.Func`2' from assembly 'MyILMergedDLL, Version=1.0.1.1, Culture=neutral, PublicKeyToken=...'.

    This is the code in my consumer project; the line that caused the TypeLoadException is the second one:

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

    The particular Row constructor that throws the TypeLoadException is defined in a public class in MyILMergedDLL, and when I use this constructor when referencing the individual project DLLs, it works fine. It is only when I use this constructor when referencing the IL-merged DLL that I get the exception. I don't know what's going on.

    Here's that constructor:

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

    And the base to which it is referring has this code:

    foreach (string key in keys.Where(
        key => !string.IsNullOrEmpty(key)
    ))
    {
        _dic.Add(key, string.Empty);
    }
    
  • Sarah Vessels
    Sarah Vessels almost 14 years
    Thanks for the info. I can't tell from looking at that page what the releases are for: VS 2010, C#, .NET 4, ILMerge?
  • Sarah Vessels
    Sarah Vessels almost 14 years
    Never mind, digging around on Mike Barnett's page I see he works on ILMerge. :)
  • Sarah Vessels
    Sarah Vessels almost 14 years
    YES! I didn't download any new release, but I'm still marking yours as the correct answer (the only one at the moment...) because it led me to visit Mike Barnett's page where he described using /targetplatform:v4,<path to your v4 framework directory> instead of /targetplatform:v4 /lib:<path to your v4 framework directory>.
  • Jeremy McGee
    Jeremy McGee over 13 years
    Here's a link to Mike's ILMerge main page: research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx
  • Contango
    Contango about 13 years
    Thanks for the comment - added /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0‌​.30319 and it worked for me as well. I'm using .NET 4.0 and Visual Studio 2010 SP1.
  • ATL_DEV
    ATL_DEV almost 13 years
    SmartAssembly may be pricey in comparison, but what is your time worth? I've spent far more money in billable hours than SmartAssembly costs to find out that the alternatives don't work and are impossible to debug. This "pricey" product is backed by quality, ease-of-use and excellent support.
  • Contango
    Contango about 12 years
    Update: Just tried this string on a rather complex project with 10 custom sub-assemblies, and it worked perfectly.
  • Despertar
    Despertar about 10 years
    For those merging with a .dll as the output, you have to change /target:exe to /target:library AND remove "$(TargetDir)$(TargetFileName)" otherwise it will load the main .dll there and in the wildcard *.dll resulting in a "ILMerge.Merge: ERROR!!: Duplicate type" error.
  • Barrett
    Barrett about 10 years
    I needed to use /targetplatform:v4,C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0 because I was deploying the application to a machine running .NET 4.0 and not 4.5 (I think): mattwrock.com/post/2012/02/29/…
  • user1703401
    user1703401 over 9 years
    Do beware that your post has a really nasty bug.
  • Mike Kelly
    Mike Kelly over 9 years
    ILMerge is now a NuGet package. If you install it from there, it is put in packages, so I modified the command line to: "$(SolutionDir)\packages\ilmerge.2.14.1208\tools\ILMerge.exe‌​"
  • brenjt
    brenjt almost 9 years
    Where do you place this file? What do you name it?
  • sebagomez
    sebagomez almost 9 years
    if not present, you need to create an ildasm.exe.config file with the xml I put in my answer