Click here to Skip to main content
11,490,003 members (50,391 online)
Click here to Skip to main content
Add your own
alternative version

Self Contained Assembly

, 13 Nov 2011 CPOL 20.3K 1.1K 41
Shows how deployment of dynamically loaded assembly can be simplified by reducing the assembly to single self contained file.
Demo.zip
Demo
AbstractPanelFactory.dll
AssemblyLoader.dll
DemoClientApp.exe
PlugIns
GradientPanelFactory.dll
SelfContainedAssembly.zip
SelfContainedAssembly
AbstractPanelFactory
bin
Release
AbstractPanelFactory.dll
AbstractPanelFactory.pdb
Properties
AssemblyLoader
bin
Release
AssemblyLoader.dll
AssemblyLoader.pdb
Properties
DemoClientApp
bin
Release
AbstractPanelFactory.dll
AbstractPanelFactory.pdb
AssemblyLoader.dll
AssemblyLoader.pdb
DemoClientApp.exe
DemoClientApp.pdb
DemoClientApp.vshost.exe
DemoClientApp.vshost.exe.manifest
PlugIns
Properties
Settings.settings
GradientPanelFactory
bin
Release
GradientPanelFactory.dll
GradientPanelFactory.pdb
DependancyAssemblies.zip
Owf.Controls.A1Panel.dll
Properties
SelfContainedAssembly.suo
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;

namespace AssemblyLoader
{
    public class Loader
    {
        static bool resolveAssembly;

        static Dictionary<string, Assembly> loadedDependancies = new Dictionary<string, Assembly>();

        static Dictionary<string, ZipAssemblyInfo> assembliesInZipResource = new Dictionary<string, ZipAssemblyInfo>();

        static ResolveEventHandler handler = FindRequiredAssembly;

        public static bool ResolveAssembly
        {
            get { return resolveAssembly; }
            set
            {
                if (resolveAssembly == value) return;

                resolveAssembly = value;

                if (value == true)
                {
                    AppDomain.CurrentDomain.AssemblyResolve += handler;
                }
                else
                {
                    AppDomain.CurrentDomain.AssemblyResolve -= handler;
                }
            }
        }

        /// <summary>
        /// AssemblyResolve Handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static Assembly FindRequiredAssembly(object sender, ResolveEventArgs args)
        {
            var requiredAssembly = new AssemblyName(args.Name);

            var shortName = requiredAssembly.Name;

            if (loadedDependancies.ContainsKey(shortName))
                return loadedDependancies[shortName];

            var assemblyFileName = shortName + ".dll";

            if (assembliesInZipResource.ContainsKey(shortName) == false)
            {
                throw new FileNotFoundException(String.Format("Unable to find {0} in zip resource of assemblies", assemblyFileName));
            }

            var info = assembliesInZipResource[shortName];

            using (var resourceStream = info.ContainingAssembly.GetManifestResourceStream(info.ManifestResourceName))
            {
                using (var zipStore = ZipStorer.Open(resourceStream, FileAccess.Read))
                {
                    using (var extractedStream = new MemoryStream())
                    {
                        zipStore.ExtractFile(zipStore.ReadCentralDir()[0], extractedStream);

                        var assemblyBytes = extractedStream.GetBuffer();

                        //Load assembly in ReflectionOnly context to verify the name before loading for execution.
                        var foundAssembly = Assembly.ReflectionOnlyLoad(assemblyBytes);

                        //Verify that we have found correct assembly before loading it.
                        //This method do not care about match of version and PublicTokenKey
                        // Optionally string comparison can be used [foundAssembly.FullName == args.Name ]
                        if (!AssemblyName.ReferenceMatchesDefinition(foundAssembly.GetName(), requiredAssembly))
                        {
                            //if more than one resolve handler are present then we can return from here
                            // otherwise we can throw exception to indicate failure .

                            // return null;
                            throw new FileNotFoundException("File Name  = " + requiredAssembly);
                        }

                        //Load assembly for execution.
                        foundAssembly = Assembly.Load(assemblyBytes);

                        loadedDependancies.Add(shortName, foundAssembly);

                        return foundAssembly;
                    }
                }
            }
        }

        public static Assembly LoadAssembly(string filePath)
        {
            if (!File.Exists(filePath)) throw new FileNotFoundException("File Name = " + filePath);

            var assembly = Assembly.LoadFrom(filePath);

            var fileNameInZip = string.Empty;
            var fileNameWithExtension = string.Empty;
            var fileNameWithoutExtension = string.Empty;

            Debug.WriteLine(String.Format("Inspecting zip files in assembly {0}.", assembly.FullName));

            foreach (var resourceName in assembly.GetManifestResourceNames())
            {
                if (resourceName.EndsWith(".zip"))
                {
                    using (var stream = assembly.GetManifestResourceStream(resourceName))
                    {
                        using (var zipStore = ZipStorer.Open(stream, FileAccess.Read))
                        {
                            foreach (var zipFileEntry in zipStore.ReadCentralDir())
                            {
                                //Full path of file in zip (i.e. including directories)
                                fileNameInZip = zipFileEntry.FilenameInZip;

                                if (fileNameInZip.EndsWith(".dll"))
                                {
                                    fileNameWithExtension = Path.GetFileName(fileNameInZip);

                                    // This is the name of assembly if above dll file.
                                    fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileNameWithExtension);

                                    if (assembliesInZipResource.ContainsKey(fileNameWithoutExtension) == false)
                                    {
                                        var info = new ZipAssemblyInfo(fileNameWithExtension, assembly, resourceName, zipFileEntry);

                                        assembliesInZipResource.Add(fileNameWithoutExtension, info);
                                    }
                                    else
                                    {
                                        var info = assembliesInZipResource[fileNameWithoutExtension];
                                        Debug.WriteLine(String.Format("Assembly {0} is already found in zip. Info = {1}", fileNameWithoutExtension, info));
                                        if (info.ZipFileEntry.Crc32 != zipFileEntry.Crc32)
                                        {
                                            var error = "Two files with same name but different Crc values are detected.";
                                            var detailMsg = String.Format("Assembly {0} and {1} both contains assembly {3} in their                                                                  embedded resource zip file and  the crc32 value for them are not same.",
                                                info.ContainingAssembly.GetName().Name, assembly.GetName().Name,
                                                fileNameWithoutExtension);

                                            Debug.Fail(error, detailMsg);

                                            throw new InvalidOperationException(error);
                                        }
                                    }

                                    //Here we are assuming that assembly name and file name will be same
                                    //i.e. Assembly with Name A will be in A.dll
                                    // So when we get A.dll in zip resource we are assuming that it contains assembly A.
                                    //In Resolve Handler  when we try to resolve Assembly A we will use the assembly name as key.

                                    // if there is any possibility that the assembly name and dll file name can be different,
                                    //then one can extract assembly and load it in reflection only context and then retrieve
                                    // assembly name. However this seems very inefficient and unnecessary as no one usually changes the
                                    //name of compiled assembly dlls.
                                }
                            }
                        }
                    }
                }
            }

            return assembly;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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

Share

About the Author

Prafulla Hunde
Software Developer Yokogawa IA Technologies India
India India
I am software developer with over 7 years exp.I have been working in industrial automation domain using MS technology.I have primarily worked on winform/WPF desktop applications.
using C# with .net framework 2.0,3.5& 4.0. I am interested and trying to get comfortable with WPF , Linq and new features..

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 13 Nov 2011
Article Copyright 2011 by Prafulla Hunde
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid