Daily Archives: 13/09/2009

Loading Assemblies In Separate Directories Into New AppDomain

As some of you may know I have been working on a code generator for my Cinch MVVM framework, which I am pleased to say I am nearly done with. The last stumbling block has been that I need to extract a bunch of Namespaces from Assemblies that the main code referenced, which I want to do by the use of Reflection which is very easy. But I also wanted the Assemblies that I would need to examine loaded into a new AppDomain so that I could Unload the newly created AppDomain when I had finished Reflecting out the Namespaces from the Assemblies.

After many failed attempted and messing around with

  • Asembly.ReflectionOnlyLoad
  • AppDomain.Load(Byte[] rawAssembly)
  • Fusion paths
  • Probing in my App.Config
  • AppDomain Evidence and AppDomainSetup

 

I finally found nirvana and hit the sweet spot, which is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Globalization;
using System.Security.Policy;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
 
namespace ConsoleApplication1
{
 
    /// <summary>
    /// Loads an assembly into a new AppDomain and obtains all the
    /// namespaces in the loaded Assembly, which are returned as a 
    /// List. The new AppDomain is then Unloaded.
    /// 
    /// This class creates a new instance of a 
    /// <c>AssemblyLoader</c> class
    /// which does the actual ReflectionOnly loading 
    /// of the Assembly into
    /// the new AppDomain.
    /// </summary>
    public class SeperateAppDomainAssemblyLoader
    {
        #region Public Methods
        /// <summary>
        /// Loads an assembly into a new AppDomain and obtains all the
        /// namespaces in the loaded Assembly, which are returned as a 
        /// List. The new AppDomain is then Unloaded
        /// </summary>
        /// <param name="assemblyLocation">The Assembly file 
        /// location</param>
        /// <returns>A list of found namespaces</returns>
        public List<String> LoadAssemblies(List<FileInfo> assemblyLocations)
        {
            List<String> namespaces = new List<String>();
 
            AppDomain childDomain = BuildChildDomain(
                AppDomain.CurrentDomain);
 
            try
            {
                Type loaderType = typeof(AssemblyLoader);
                if (loaderType.Assembly != null)
                {
                    var loader = 
                        (AssemblyLoader)childDomain.
                            CreateInstanceFrom(
                            loaderType.Assembly.Location, 
                            loaderType.FullName).Unwrap();
 
                    namespaces = loader.LoadAssemblies(
                        assemblyLocations);
                }
                return namespaces;
            }
 
            finally
            {
 
                AppDomain.Unload(childDomain);
            }
        }
        #endregion
 
        #region Private Methods
        /// <summary>
        /// Creates a new AppDomain based on the parent AppDomains 
        /// Evidence and AppDomainSetup
        /// </summary>
        /// <param name="parentDomain">The parent AppDomain</param>
        /// <returns>A newly created AppDomain</returns>
        private AppDomain BuildChildDomain(AppDomain parentDomain)
        {
            Evidence evidence = new Evidence(parentDomain.Evidence);
            AppDomainSetup setup = parentDomain.SetupInformation;
            return AppDomain.CreateDomain("DiscoveryRegion", 
                evidence, setup);
        }
        #endregion
 
 
        /// <summary>
        /// Remotable AssemblyLoader, this class 
        /// inherits from <c>MarshalByRefObject</c> 
        /// to allow the CLR to marshall
        /// this object by reference across 
        /// AppDomain boundaries
        /// </summary>
        class AssemblyLoader : MarshalByRefObject
        {
            #region Private/Internal Methods
            /// <summary>
            /// ReflectionOnlyLoad of single Assembly based on 
            /// the assemblyPath parameter
            /// </summary>
            /// <param name="assemblyPath">The path to the Assembly</param>
            [SuppressMessage("Microsoft.Performance", 
                "CA1822:MarkMembersAsStatic")]
            internal List<String> LoadAssemblies(
                List<FileInfo> assemblyLocations)
            {
                List<String> namespaces = new List<String>();
                try
                {
                    foreach (FileInfo assemblyLocation in 
                        assemblyLocations)
                    {
                        Assembly.ReflectionOnlyLoadFrom(
                            assemblyLocation.FullName);
                    }
 
                    foreach (Assembly reflectionOnlyAssembly 
                        in AppDomain.CurrentDomain.
                            ReflectionOnlyGetAssemblies())
                    {
                        foreach (Type type in 
                            reflectionOnlyAssembly.GetTypes())
                        {
                            if (!namespaces.Contains(
                                type.Namespace))
                                namespaces.Add(
                                    type.Namespace);
                        }                   
                    }
                    return namespaces;
                }
                catch (FileNotFoundException)
                {
                    /* Continue loading assemblies even if an assembly
                     * can not be loaded in the new AppDomain. */
                    return namespaces;
                }
            }
            #endregion
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Which you can use like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            SeperateAppDomainAssemblyLoader 
                appDomainAssemblyLoader = 
                new SeperateAppDomainAssemblyLoader();
 
            List<FileInfo> assemblies = new List<FileInfo>();
            String root = @"C:UserssachaDesktopAppDomainMonkery";
            assemblies.Add(new FileInfo(root + 
                @"ConsoleApplication1ClassLibrary1binDebugClassLibrary1.dll"));
            assemblies.Add(new FileInfo(root + 
                @"ConsoleApplication1ClassLibrary2binDebugClassLibrary2.dll"));
 
            foreach (String @namespace in 
                appDomainAssemblyLoader.
                    LoadAssemblies(assemblies))
            {
                Console.WriteLine(String.Format(
                    "Namespace found : {0}", @namespace));
            }              
            Console.ReadLine();
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Which as you can see results in the following :

image

 

I think this code is pretty useful and I hope you find as much use for it as I have. It took me long enough to figure this out, and many google searches were done and much consulting of APIs/picking friends knowledge was done to bring you this code, so enjoy it. It nearly killed me getting that one to work.

As usual here is a small demo app : http://sachabarber.net/wp-content/uploads/2009/09/AppDomainMonkery.zip

Advertisements