Click here to Skip to main content
Click here to Skip to main content

Tagged as

Load DLL From Embedded Resource

, 18 Jul 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Merge DLL with App Into Single EXE

 

Content

 

  • Introduction
  • When and Why Embedding is needed? 
  • Add DLL As Embedded Resource
  • Lets Start Coding
  • Dive Into EmbeddedAssembly.cs
  • Class of EmbeddedAssembly.cs
  • Related Tips 
  • Alternatives
  • Article History

 

Introduction


In an ordinary way, when we added a reference of a custom built or third party DLL, it will be distributed along with our main EXE application at the same folder. If the DLL is not existed in the same folder with the EXE application, an exception will be thrown. There are some cases that we wish to pack / include the DLL within the EXE application. Therefore, this article introduces a solution of loading DLLs from embedded resources. 

Example of Exception (DLL is not found at the same folder of distributed EXE Application):

 

When and Why Embedding is needed?


Merging DLLs is commonly used in developing portable software. 

Advantages 

  1. Easy to distribute among end-user. (Copying 1 single file instead of multiple files) 
  2. Dependent DLLs and files will not lost or accidentally left out while end-user copying the software. 
  3. End-user will not messed up the files with other non-related files. 

 

Disadvantages 

 

  1. Increase the complexity of work for developers. Developers need to take extra steps to merge the files. 
  2. If some of the component has a new version. The whole thing need to be repacked. If the DLLs are not merged, it is easy for developers to change the parts. (However, that would be the hard thing to do for an end-user, end-user would prefer just download the whole thing in stead of manually replace the DLLs.) 

 

Embedding DLLs has the same concept of most software installer. Most of the installer are packed into one single file. The reason of this is cut down complexity and make it more easier to distribute.

 

Add DLL As Embedded Resource


Basic intro about DLLs used as examples in this article. 

 

    • System.Windows.Forms.Ribbon35.DLL 
    • System.Data.SQLite.DLL
      • - Type: Mixed Code/Unmanaged DLL
      • - An Open Source Cross Platform Embedded Database Engine Wrapper for .NET
      • - Read more: http://system.data.sqlite.org 

Before any coding started, the DLLs have to be added into the project.

First, add the DLL as Reference.

 

Then, add the same DLL as file into the project. Right click the project's name > Add > Existing Item... 

 

The same DLL will exist twice in different folder in the project.

As the below picture shown,

for Referenced DLL, in the properties explorer, change Copy Local = False

for Added DLL as File, in the properties explorer, change Build Action = Embedded Resource  

 

Do the same thing to System.Data.SQLite.DLL. Add it into project's file and as reference. Change the properties as System.Windows.Forms.Ribbon35.DLL. 

Note: If you have any other unmanaged / native DLL that is not able to be referenced, then you won't have to reference it, just add the Unmanaged DLL as embedded resource. 

Lets Start Coding   


Obtain EmbeddedAssembly.cs from download and add it into project. 

or browse to bottom of this article and copy the code of  EmbeddedAssembly.cs. 

Open Program.cs 

Initial code of Program.cs

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

  Load the DLL from Embedded Resource into Memory. Use EmbeddedAssembly.Load to load it into memory. 

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        string resource1 = "MyApp.System.Windows.Forms.Ribbon35.dll";
        string resource2 = "MyApp.System.Data.SQLite.dll";
        EmbeddedAssembly.Load(resource1, "System.Windows.Forms.Ribbon35.dll");
        EmbeddedAssembly.Load(resource2, "System.Data.SQLite.dll");

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

Take note about the format of the resource string. Example: 

MyApp.System.Windows.Forms.Ribbon35.dll  

This string is the Embedded Resource address in the project. 

MyApp is the project name, followed by the DLL filename. If the DLL is added inside a folder, the folder's name must be included in the resource string. Example:

MyApp.NewFolder1.System.Windows.Forms.Ribbon35.dll 

The DLLs are not distributed with the application, when the application fails to locate the DLL, it raises an Event of  AppDomain.CurrentDomain.AssemblyResolve. AssemblyResolve is requesting the missing DLL. Then, we will tell our application to look for the DLL in memory. Use EmbeddedAssembly.Get to retrieve the DLL from memory and pass it for AssemblyResolve to handle the rest.  

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        string resource1 = "MyApp.System.Windows.Forms.Ribbon35.dll";
        string resource2 = "MyApp.System.Data.SQLite.dll";
        EmbeddedAssembly.Load(resource1, "System.Windows.Forms.Ribbon35.dll");
        EmbeddedAssembly.Load(resource2, "System.Data.SQLite.dll");
 
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
 
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);S
        Application.Run(new Form1());
    }
 
    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        return EmbeddedAssembly.Get(args.Name);
    }
}

Thats it.  

Dive Into EmbeddedAssembly.cs 


Here, we shall take a look at what is EmbeddedAssembly.cs doing behind. 

There are two common type of DLLs that will be used with .NET application

 

  • Managed DLL
  • Unmanaged DLL or Mixed Code DLL 

 

Managed DLL is fully written by .NET language (i.e. C#, VB.NET)
Unmanaged DLL will be written by other languages (i.e. C, C++)

System.Data.SQLite.DLL is Mixed Code. It is a combination of C and C#. Therefore it can be considered as Unmanaged DLL.     

Loading Managed DLL

Procedure of Loading Managed DLL from Embedded Resource is simple. 

 

static class Program
{
    [STAThread]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        return Load();
    }
} 

 

public static Assembly Load()
{
    byte[] ba = null;
    string resource = "MyApp.System.Windows.Forms.Ribbon35.dll";
    Assembly curAsm = Assembly.GetExecutingAssembly();
    using (Stream stm = curAsm.GetManifestResourceStream(resource))
    {
        ba = new byte[(int)stm.Length];
        stm.Read(ba, 0, (int)stm.Length);
 
        return Assembly.Load(ba);
    }
}

Loading Unmanaged DLL

Unmanaged DLL cannot be loaded directly from stream/byte[] as Managed DLL do. 

 

  1. First load the DLL from Embedded Resource into byte[].
  2. Write byte[] into a physical file and stored it at temp folder.
  3. Use Assembly.LoadFile() to load the file into memory.   

 

Steps are explained as comments inside codes below: 

 

public static Assembly Load()
{
    // Get the byte[] of the DLL
    byte[] ba = null;
    string resource = "MyApp.System.Data.SQLite.dll";
    Assembly curAsm = Assembly.GetExecutingAssembly();
    using (Stream stm = curAsm.GetManifestResourceStream(resource))
    {
        ba = new byte[(int)stm.Length];
        stm.Read(ba, 0, (int)stm.Length);
    }
 
    bool fileOk = false;
    string tempFile = "";
 
    using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
    {
        // Get the hash value of the Embedded DLL
        string fileHash = BitConverter.ToString(sha1.ComputeHash(ba)).Replace("-", string.Empty);
 
        // The full path of the DLL that will be saved
        tempFile = Path.GetTempPath() + "System.Data.SQLite.dll";
 
        // Check if the DLL is already existed or not?
        if (File.Exists(tempFile))
        {
            // Get the file hash value of the existed DLL
            byte[] bb = File.ReadAllBytes(tempFile);
            string fileHash2 = BitConverter.ToString(sha1.ComputeHash(bb)).Replace("-", string.Empty);
 
            // Compare the existed DLL with the Embedded DLL
            if (fileHash == fileHash2)
            {
                // Same file
                fileOk = true;
            }
            else
            {
                // Not same
                fileOk = false;
            }
        }
        else
        {
            // The DLL is not existed yet
            fileOk = false;
        }
    }
 
    // Create the file on disk
    if (!fileOk)
    {
        System.IO.File.WriteAllBytes(tempFile, ba);
    }
    
    // Load it into memory    
    return Assembly.LoadFile(tempFile);
} 

EmbeddedAssembly.cs will pre-load the required DLL and stored it inside a Dictionary. When the application's Event of AssemblyResolve is raised, EmbeddedAssembly.Get() will return the DLL as requested. 

Class of EmbeddedAssembly.cs


using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;

public class EmbeddedAssembly
{
    // Version 1.3

    static Dictionary<string, Assembly> dic = null;

    public static void Load(string embeddedResource, string fileName)
    {
        if (dic == null)
            dic = new Dictionary<string, Assembly>();

        byte[] ba = null;
        Assembly asm = null;
        Assembly curAsm = Assembly.GetExecutingAssembly();

        using (Stream stm = curAsm.GetManifestResourceStream(embeddedResource))
        {
            // Either the file is not existed or it is not mark as embedded resource
            if (stm == null)
                throw new Exception(embeddedResource + " is not found in Embedded Resources.");

            // Get byte[] from the file from embedded resource
            ba = new byte[(int)stm.Length];
            stm.Read(ba, 0, (int)stm.Length);
            try
            {
                asm = Assembly.Load(ba);

                // Add the assembly/dll into dictionary
                dic.Add(asm.FullName, asm);
                return;
            }
            catch
            {
                // Purposely do nothing
                // Unmanaged dll or assembly cannot be loaded directly from byte[]
                // Let the process fall through for next part
            }
        }

        bool fileOk = false;
        string tempFile = "";

        using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
        {
            string fileHash = BitConverter.ToString(sha1.ComputeHash(ba)).Replace("-", string.Empty);;
            
            tempFile = Path.GetTempPath() + fileName;

            if (File.Exists(tempFile))
            {
                byte[] bb = File.ReadAllBytes(tempFile);
                string fileHash2 = BitConverter.ToString(sha1.ComputeHash(bb)).Replace("-", string.Empty);

                if (fileHash == fileHash2)
                {
                    fileOk = true;
                }
                else
                {
                    fileOk = false;
                }
            }
            else
            {
                fileOk = false;
            }
        }

        if (!fileOk)
        {
            System.IO.File.WriteAllBytes(tempFile, ba);
        }
        
        asm = Assembly.LoadFile(tempFile);

        dic.Add(asm.FullName, asm);
    }

    public static Assembly Get(string assemblyFullName)
    {
        if (dic == null || dic.Count == 0)
            return null;

        if (dic.ContainsKey(assemblyFullName))
            return dic[assemblyFullName];

        return null;
    }
}

Related Tips


Tip 1: Get Assembly within another assembly (Suggested by Thierry Maurel

In case of DLLs embedded into another DLL, your can use

Assembly a1 = GetType ().Assembly; 

to obtain the local dll assembly, instead of the executable one.

Alternatives

 


ILMerge 

Mergebin - Combine your Unmanaged and Mananaged DLLs into one Handy Package 

Article - Calling Managed Code from Unmanaged Code and vice-versa 

SmartAssembly (Commercial) 

Costura (Suggested by herves

Pack All Extra Files/DLLs as Archive File - Zip/CAB/Tar (Suggested by Fred Flams)

  • Pack the DLLs into Archive File. During the start-up of application unpack the archive file into memory or on disk.
  • Main EXE will be lighter. Distribute the archive file with same root name of the main EXE. End-user will know the 2 files (EXE and Archive File) must go together. 

Convert DLL into Encrypted Class and Load from Class at Runtime   

Vitevic Assembly Embedder

 

 

 

Article History 


 

 

  • 16 Jan 2013 - Initial work. 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

adriancs
Software Developer
Malaysia Malaysia
Writing programs is an art.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberĐinh Công Thắng18-Feb-13 16:05 
GeneralMy vote of 5 PinmemberMihai MOGA16-Feb-13 20:18 
QuestionNot Work on Win XP [modified] PinmemberMember 454471929-Jan-13 0:13 
GeneralMy vote of 5 Pinmembercaucow25-Jan-13 12:47 
GeneralRe: My vote of 5 Pinmvpadriancs25-Jan-13 19:51 
QuestionDictionary ctor error? PingroupPaul @ The Computer Station25-Jan-13 1:37 
AnswerRe: Dictionary ctor error? Pinmvpadriancs25-Jan-13 19:43 
SuggestionA different way of packing PinmemberFred Flams17-Jan-13 3:50 
GeneralRe: A different way of packing Pinmvpadriancs18-Jan-13 3:14 
SuggestionRe: A different way of packing PinmemberLogi Guna11-Feb-13 3:32 
QuestionAdvantages PinmemberSimon Tan16-Jan-13 11:44 
AnswerRe: Advantages Pinmvpadriancs16-Jan-13 15:26 
AnswerRe: Advantages Pinmvpadriancs16-Jan-13 15:53 
Questionwhen people like to embed their dll into resource PinmemberTridip Bhattacharjee16-Jan-13 9:48 
i like to know in which kind of situation people like to embed dll into resource. tell me few scenario. thanks
tbhattacharjee

AnswerRe: when people like to embed their dll into resource Pinmvpadriancs16-Jan-13 15:23 
AnswerRe: when people like to embed their dll into resource Pinmvpadriancs16-Jan-13 15:54 
GeneralCostura Pinmemberherves16-Jan-13 1:01 
GeneralRe: Costura Pinmvpadriancs16-Jan-13 3:18 
GeneralRe: Costura Pinmemberherves16-Jan-13 3:20 
GeneralRe: Costura Pinmvpadriancs16-Jan-13 3:21 
GeneralRe: Costura Pinmemberherves20-Feb-13 2:00 
QuestionGood job ! PinmemberThierry Maurel15-Jan-13 23:32 
AnswerRe: Good job ! PinmemberElaheh Ordoni26-Jul-14 2:30 
GeneralRe: Good job ! PinmemberThierry Maurel28-Jul-14 2:26 
GeneralRe: Good job ! PinmemberElaheh Ordoni1-Aug-14 19:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 18 Jul 2014
Article Copyright 2013 by adriancs
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid