Embedding one dll inside another as an embedded resource and then calling it from my code

C#.Net 3.5Dll

C# Problem Overview

I've got a situation where I have a DLL I'm creating that uses another third party DLL, but I would prefer to be able to build the third party DLL into my DLL instead of having to keep them both together if possible.

This with is C# and .NET 3.5.

The way I would like to do this is by storing the third party DLL as an embedded resource which I then place in the appropriate place during execution of the first DLL.

The way I originally planned to do this is by writing code to put the third party DLL in the location specified by System.Reflection.Assembly.GetExecutingAssembly().Location.ToString() minus the last /nameOfMyAssembly.dll. I can successfully save the third party .DLL in this location (which ends up being

> C:\Documents and Settings\myUserName\Local Settings\Application > Data\assembly\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901

), but when I get to the part of my code requiring this DLL, it can't find it.

Does anybody have any idea as to what I need to be doing differently?

C# Solutions

Solution 1 - C#

Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve, load the resource using Assembly.GetManifestResourceStream and feed its content as a byte array into the corresponding Assembly.Load overload. Below is how one such implementation could look like in C#:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    var resName = args.Name + ".dll";    
    var thisAssembly = Assembly.GetExecutingAssembly();    
    using (var input = thisAssembly.GetManifestResourceStream(resName))
        return input != null 
             ? Assembly.Load(StreamToBytes(input))
             : null;

where StreamToBytes could be defined as:

static byte[] StreamToBytes(Stream input) 
    var capacity = input.CanSeek ? (int) input.Length : 0;
    using (var output = new MemoryStream(capacity))
        int readLength;
        var buffer = new byte[4096];

            readLength = input.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, readLength);
        while (readLength != 0);

        return output.ToArray();

Finally, as a few have already mentioned, ILMerge may be another option to consider, albeit somewhat more involved.

Solution 2 - C#

In the end I did it almost exactly the way raboof suggested (and similar to what dgvid suggested), except with some minor changes and some omissions fixed. I chose this method because it was closest to what I was looking for in the first place and didn't require using any third party executables and such. It works great!

This is what my code ended up looking like:

EDIT: I decided to move this function to another assembly so I could reuse it in multiple files (I just pass in Assembly.GetExecutingAssembly()).

This is the updated version which allows you to pass in the assembly with the embedded dlls.

embeddedResourcePrefix is the string path to the embedded resource, it will usually be the name of the assembly followed by any folder structure containing the resource (e.g. "MyComapny.MyProduct.MyAssembly.Resources" if the dll is in a folder called Resources in the project). It also assumes that the dll has a .dll.resource extension.

   public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add =>
            try {
                string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
                using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
                    return input != null
                         ? Assembly.Load(StreamToBytes(input))
                         : null;
            } catch (Exception ex) {
                _log.Error("Error dynamically loading dll: " + args.Name, ex);
                return null;
        }; // Had to add colon

    private static byte[] StreamToBytes(Stream input) {
        int capacity = input.CanSeek ? (int)input.Length : 0;
        using (MemoryStream output = new MemoryStream(capacity)) {
            int readLength;
            byte[] buffer = new byte[4096];

            do {
                readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length
                output.Write(buffer, 0, readLength);
            while (readLength != 0);

            return output.ToArray();

Solution 3 - C#

There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Then you can just make a build event similar to the following.

Set Path="C:\Program Files\Microsoft\ILMerge"

ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $(ProjectDir)\bin\Release\release.exe $(ProjectDir)\bin\Release\InteractLib.dll $(ProjectDir)\bin\Release\SpriteLib.dll $(ProjectDir)\bin\Release\LevelLibrary.dll

Solution 4 - C#

I've had success doing what you are describing, but because the third-party DLL is also a .NET assembly, I never write it out to disk, I just load it from memory.

I get the embedded resource assembly as a byte array like so:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

        byte[] assemblyData;
        using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
            assemblyData = ReadBytesFromStream(stream);

Then I load the data with Assembly.Load().

Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.

See the .NET Fusion Workshop for additional details.

Solution 5 - C#

You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.

Solution 6 - C#

Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.


All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLawrence JohnstonView Question on Stackoverflow
Solution 1 - C#Atif AzizView Answer on Stackoverflow
Solution 2 - C#Lawrence JohnstonView Answer on Stackoverflow
Solution 3 - C#FostahView Answer on Stackoverflow
Solution 4 - C#dgvidView Answer on Stackoverflow
Solution 5 - C#Mark SmithView Answer on Stackoverflow
Solution 6 - C#HallgrimView Answer on Stackoverflow