3 Comments

.NET Power Tip 10: Merging Assemblies

Say, you have a .NET Project with a lot of assemblies in the project output and you don’t like it. For your personal reasons, you would like to just deliver one assembly that contains your entire project.

There are several ways to do so. This post presents three methods: ILMerge.exe, Embedded Resource and AppDomain.ResolveAssembly and Fody.Costura.

 

If you are interested in the approache I like most, skip down to Fody.Costura!

 

1. ILMerge.exe

IL-Merge is a command line application that can be used to combine several assemblies into one.

You can download it here: https://www.microsoft.com/en-us/download/details.aspx?id=17630

and find out how it works here: http://www.codeproject.com/Articles/9364/Merging-NET-assemblies-using-ILMerge

The official page with the documentation can be found here: http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx

There is even a GUI that takes the command line fiddling away: https://ilmergegui.codeplex.com/

 

However, there are some problems with ILMerge, that are discussed on StackOverflow: http://stackoverflow.com/questions/9376/ilmerge-best-practices

The main issues are:

  • ILMerge strips XML comments from code files
  • It doesn’t correctly handle PDB files
  • It fails with XAML files in the solution
  • It doesn’t guarantee the correctness of the merge. You need to run PEVerify after each merge operation.

Lets look at the next approach that does not require any third party tools.

 

2. Embedded Resource and AppDomain.ResolveAssembly, aka the Jeffrey Richter approach

In his book “CLR via C#”, Jeffrey Richter describes a sexy approach:

http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx

Basically, you add the assemblies to the project and set their Build Action to “Embedded Resource”. Then you register a callback method with the ResolveAssembly event of the AppDomain class, like so:

 

image

 

An improved version of this approach with less manual work is implemented in the Nuget package Fody.Costura that is presented in the next section.

 

3. Fody.Costura

Fody is a .NET Assembly Weaver. Think of a tool that assists you in metaprogramming. And by metaprogramming I mean modifying .NET assemblies on the intermediate language (MSIL) level, after the build process. Fody is designed as an extendable platform with a long list of plugins. Check out the full list on https://github.com/Fody/Fody/

 

Costura is one of these plugins that can be used to merge assemblies as embedded resources.

It makes two changes:

1. Includes all assemblies and pdb files with “Copy Local” as resources in the target assembly.

2. Injects code into the module initializer to load the included assemblies.

 

Lets use Fody.Costura in an example project:

We have a demo project that uses log4net, Google Maps and some other 3rd party assemblies. The project output looks like this:

image

 

Usage is trivial:

1. Add the nuget package Costura.Fody to your project

2. Done!

 

That is it. From now on your referenced assemblies are included in the main project assembly. Taking a look at IlSpy (www.ilspy.com) reveals the included resources and the AssemblyLoader class that has been added by Costura.

image

 

However, you might be disappointed at first. The project output has not changed at all. Its still the same:

image

 

Fody.Costura does include the referenced assemblies into MergeAssemblies1.exe but ist DOES NOT clean up the project output directory. Therefore, all the referenced assemblies will still be there. You can copy MergeAssemblies1.exe to another location and execute it from there.

With a couple of lines, you can add a cleanup target to your *.csproj file. Add this code:

<Target 
    AfterTargets="AfterBuild;NonWinFodyTarget"
    Name="CleanReferenceCopyLocalPaths" >
     <Delete Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
</Target>

And voilà, a beautifully clean output directory:

image

IMPORTANT HINT!

Fody.Costura does only include assemblies referenced by your top project. If project MergeAssemblies1.exe would reference another project, let’s call it “ProjectB” which references a third party assembly such as Entity Framework, you would need to add Entity Framework to your top level project (MergeAssemblies1) for Costura to include it.

Great Stuff!

  • tlwest

    One caveat, it appears that at run-time, the executable only attempts to load the DLLs bound into the assembly if it can’t find them in the same directory. This means that if the EXE is copied into a directory with old versions of the DLLs, you can unexpectedly be running old code!

  • Thats a good point. Thanks for your comment.

  • geocine

    is there a way to embed images like *.png, *.jpg, *.ico using this or any other way?