WPF: How to combine multiple assemblies into a single exe

At work, I am currently working on a small WPF app that we want to keep as a simple standalone exe. However, it has dependencies on several third-party assemblies: SharpZipLib, Unity, ServiceLocator, etc.

Microsoft has handy tool called ILMerge that merges multiple .NET assemblies into a single dll or exe. Unfortunately, it doesn’t support WPF applications, because of the way XAML is compiled.

Instead, you can use another approach — include all your referenced third-party assemblies as embedded resources of the exe:

public partial class App : Application
{
    private void OnStartup(object sender, StartupEventArgs e)
    {
        AppDomain.CurrentDomain.AssemblyResolve += 
            new ResolveEventHandler(ResolveAssembly);

        // proceed starting app...
    }

    static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly parentAssembly = Assembly.GetExecutingAssembly();

        var name = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
        var resourceName = parentAssembly.GetManifestResourceNames()
            .First(s => s.EndsWith(name));

        using (Stream stream = parentAssembly.GetManifestResourceStream(resourceName))
        {
            byte[] block = new byte[stream.Length];
            stream.Read(block, 0, block.Length);
            return Assembly.Load(block);
        }
    }
}

Whenever .NET can’t find a referenced assembly, it will call our code and we can provide an Assembly instance ourselves. Note this code expects a sane DLL-naming convention :)

Update: Dan Chambers just tweeted about an improved and expanded version of this approach that automatically embeds referenced assemblies, and adds the assembly resolve hook at an earlier point in the app startup process.

May 14, 2009

11 Comments

mojtaba on June 2, 2009 at 12:23 am.

It is a funny creative solution thanks Mr.

Omar Shahine on September 22, 2009 at 12:36 pm.

Awesome tip. Even better than ILMerge.

Markus on May 11, 2010 at 4:16 pm.

Hi Richard,

cool idea and it is working for me if I Add the following code at the beginning of the Load Resources handler:

if((bool)TryFindResource(args.Name) == true) {
return FindResource(args.Name) as Assembly;
}
But if I reference a style in App.xaml which is defined in a component loaded it is not wrking. because the OnStartup Event is fired after the Resources in the App.xaml are resolved… ???

Do you have any suggestions?

Nice greetings, Markus

Richard on May 11, 2010 at 4:36 pm.

Markus: OnStartup may be too late in the WPF application lifecycle. Try binding the event in the App constructor instead?

Markus on May 11, 2010 at 5:02 pm.

Richard: Thanks for your very quick response! I’l give it a try…

Markus on May 12, 2010 at 8:49 am.

Thanks again! It is working like a charm now!

A little sample solution can be downloaded and used (for free) on my blog… ( see http://blog.mahop.net/post/Merge-WPF-Assemblies.aspx )

Nice greetings from Germany,
Markus

Pz on October 1, 2010 at 10:33 am.

Hi, thanks a lot for that one! I am not sure, but when the same assembly demanded from different places, isn’t the resource being read several times? If yes, simple caching is way to go.

Anyway thanks for a cool idea ;-)

Sven G on May 24, 2012 at 8:03 am.

Hi,

this is very great!!

Thank you

shyamala on June 26, 2012 at 12:04 pm.

Hi..

I want to get the path of the .dll file used in one of my wpf application..
I mean the Debug folder path of that .dll being created in some other application……..
plz help me……..

Shane on February 18, 2013 at 7:35 pm.

Hi Richard,
I followed your steps but unfortunately can’t get this to work. I’m new to programming so don’t understand the background to most of this but I’ve added your code to App.xaml.cs and added the DLL to the Project as Embedded Resource. Everything compiles fine and the program runs ok when the DLL is in the same folder as the EXE. However, if I remove the DLL from the folder, the program will crash when it calls a function from the DLL. Any advise on what I can do to trouble shoot this?

Thanks
Shane

Richard on February 19, 2013 at 8:39 am.

Hi Shane, set a breakpoint in ResolveAssembly() and see what happens. It might be failing to find the embedded resource.

Leave a Reply