Coder Perfect

DLLs embedded in a built executable

Problem

Is it possible to include a pre-existing DLL in a compiled C# executable (thus reducing the number of files to distribute)? How would one go about achieving it if it is possible?

Normally, I’m fine with leaving the DLLs outside and letting the setup program handle everything, but a couple of folks at work have asked me about it, and I’m not sure.

Asked by Merus

Solution #1

I strongly advise you to utilize Costura. Fody is by far the simplest and most straightforward approach to include resources in your assembly. It comes as a NuGet package.

Install-Package Costura.Fody

It will automatically embed all references copied to the output directory into your main assembly after you add it to the project. You might wish to add a target to your project to clear up the embedded files:

Install-CleanReferencesTarget

You’ll also be able to specify whether the pdbs should be included, whether certain assemblies should be excluded, and whether the assemblies should be extracted on the fly. Unmanaged assemblies, as far as I’m aware, are likewise supported.

Update

Some individuals are currently working on adding DNX support.

Update 2

MSBuild 16 is required for the most recent Fody version (so Visual Studio 2019). MSBuild 15 will be supported by Fody version 4.2.1. (reference: Fody is only supported on MSBuild 16 and above. Current version: 15)

Answered by Matthias

Solution #2

Simply choose Project Properties -> Resources -> Add Resource -> Add Existing File… from the context menu of your Visual Studio project. Also, add the following code to your App.xaml.cs or equivalent.

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");

    dllName = dllName.Replace(".", "_");

    if (dllName.EndsWith("_resources")) return null;

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());

    byte[] bytes = (byte[])rm.GetObject(dllName);

    return System.Reflection.Assembly.Load(bytes);
}

Here’s the link to my first blog post: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

Answered by Lars Holm Jensen

Solution #3

You can use ILMerge to merge them if they’re managed assemblies. You’ll have to put in a little more effort using native DLLs.

Also see: How can I combine a C++ windows dll into a C# program exe?

Answered by Shog9

Solution #4

Yes, you can combine.NET executables with libraries. There are a variety of tools available to help you complete the task:

Furthermore, this can be used with the Mono Linker, which removes superfluous code and so reduces the size of the final assembly.

Another option is to use.NETZ, which can not only compress an assembly but also pack the dlls directly into the executable. The difference between the options given above is that. They are not merged by NETZ; they remain separate assemblies that are packaged together as a single package.

Answered by Bobby

Solution #5

ILMerge can merge assemblies into a single one if the assembly contains solely managed code. You can use the command-line app, or you can add a reference to the executable and combine it programmatically. Eazfuscator and.Netz, both of which are free, provide a GUI version. BoxedApp and SmartAssembly are two paid apps.

I would recommend SmartAssembly if you need to integrate assemblies with unmanaged code. I never had any problems with SmartAssembly, but I did with everything else. It can now incorporate the necessary dependencies as resources in your main executable.

By embedding dll to your resources and then depending on AppDomain’s Assembly ResolveHandler, you may accomplish all of this manually without worrying about whether the assembly is managed or in mixed mode. By embracing the worst case scenario, i.e. assemblies with unmanaged code, this is a one-stop solution.

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        string assemblyName = new AssemblyName(args.Name).Name;
        if (assemblyName.EndsWith(".resources"))
            return null;

        string dllName = assemblyName + ".dll";
        string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);

            //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

            File.WriteAllBytes(dllFullPath, data);
        }

        return Assembly.LoadFrom(dllFullPath);
    };
}

The key is to save the bytes to a file and then load them from there. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath() is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just ‘cos

For managed dlls, you need not write bytes, but directly load from the location of the dll, or just read the bytes and load the assembly from memory. Like it or not:

    using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
    {
        byte[] data = new byte[stream.Length];
        s.Read(data, 0, data.Length);
        return Assembly.Load(data);
    }

    //or just

    return Assembly.LoadFrom(dllFullPath); //if location is known.

If the assembly is completely unmanaged, see this page or this site for instructions on how to load such dlls.

Answered by nawfal

Post is based on https://stackoverflow.com/questions/189549/embedding-dlls-in-a-compiled-executable