65.9K
CodeProject 正在变化。 阅读更多。
Home

将位于不同目录中的程序集加载到新的 AppDomain

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (9投票s)

Sep 13, 2009

CPOL

1分钟阅读

viewsIcon

71522

如何将位于不同目录中的程序集加载到新的 AppDomain

正如一些人可能知道的,我一直在为我的 Cinch MVVM 框架开发一个代码生成器,我很高兴地说我快完成了。最后的障碍是,我需要从主代码引用的程序集中提取一堆命名空间,我想通过使用反射来实现,这非常容易。但我还希望将需要检查的程序集加载到新的 AppDomain 中,以便在我完成从程序集中反射命名空间后,可以卸载新创建的 AppDomain

经过多次失败的尝试和折腾之后,

  • Asembly.ReflectionOnlyLoad
  • AppDomain.Load(Byte[] rawAssembly)
  • Fusion 路径
  • 在我的 App.Config 中探测
  • AppDomain Evidence 和 AppDomainSetup

我终于找到了天堂,找到了最佳方案,如下所示

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> LoadAssembly(FileInfo assemblyLocation)
        {
            List<String> namespaces = new List<String>();

            if (string.IsNullOrEmpty(assemblyLocation.Directory.FullName))
            {
                throw new InvalidOperationException(
                    "Directory can't be null or empty.");
            }

            if (!Directory.Exists(assemblyLocation.Directory.FullName))
            {
                throw new InvalidOperationException(
                   string.Format(CultureInfo.CurrentCulture,
                   "Directory not found {0}", 
                   assemblyLocation.Directory.FullName));
            }

            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();

                    loader.LoadAssembly(
                        assemblyLocation.FullName);
                    namespaces = 
                        loader.GetNamespaces(
                        assemblyLocation.Directory.FullName);
                }
                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>
            /// Gets namespaces for ReflectionOnly Loaded Assemblies
            /// </summary>
            /// <param name="path">The path to the Assembly</param>
            /// <returns>A List of namespace strings</returns>
            [SuppressMessage("Microsoft.Performance", 
                "CA1822:MarkMembersAsStatic")]
            internal List<String> GetNamespaces(string path)
            {
                List<String> namespaces = new List<String>();

                DirectoryInfo directory = new DirectoryInfo(path);
                ResolveEventHandler resolveEventHandler = 
                    (s,e)=> { 
                                return OnReflectionOnlyResolve(
                                    e, directory); 
                            };

                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve 
                    += resolveEventHandler;

                Assembly reflectionOnlyAssembly = 
                    AppDomain.CurrentDomain.
                        ReflectionOnlyGetAssemblies().First();
                
                foreach (Type type in reflectionOnlyAssembly.GetTypes())
                {
                    if (!namespaces.Contains(type.Namespace))
                        namespaces.Add(type.Namespace);
                }

                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve 
                    -= resolveEventHandler;
                return namespaces;
            }

            /// <summary>
            /// Attempts ReflectionOnlyLoad of current 
            /// Assemblies dependants
            /// </summary>
            /// <param name="args">ReflectionOnlyAssemblyResolve 
            /// event args</param>
            /// <param name="directory">The current Assemblies 
            /// Directory</param>
            /// <returns>ReflectionOnlyLoadFrom loaded
            /// dependant Assembly</returns>
            private Assembly OnReflectionOnlyResolve(
                ResolveEventArgs args, DirectoryInfo directory)
            {
                Assembly loadedAssembly = 
                    AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
                        .FirstOrDefault(
                          asm => string.Equals(asm.FullName, args.Name, 
                              StringComparison.OrdinalIgnoreCase));

                if (loadedAssembly != null)
                {
                    return loadedAssembly;
                }

                AssemblyName assemblyName = 
                    new AssemblyName(args.Name);
                string dependentAssemblyFilename = 
                    Path.Combine(directory.FullName, 
                    assemblyName.Name + ".dll");

                if (File.Exists(dependentAssemblyFilename))
                {
                    return Assembly.ReflectionOnlyLoadFrom(
                        dependentAssemblyFilename);
                }
                return Assembly.ReflectionOnlyLoad(args.Name);
            }

            /// <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 void LoadAssembly(String assemblyPath)
            {
                try
                {
                    Assembly.ReflectionOnlyLoadFrom(assemblyPath);
                }
                catch (FileNotFoundException)
                {
                    /* Continue loading assemblies even if an assembly
                     * can not be loaded in the new AppDomain. */
                }
            }
            #endregion
        }
    }
}

我认为这段代码非常有用,希望你也能像我一样找到它的用处。我花了很长时间才弄清楚,进行了许多 Google 搜索,并咨询了 API/向朋友请教,才为你带来这段代码,所以请好好享受它。为了让它工作,我差点没累死。

像往常一样,这里有一个小演示应用程序

© . All rights reserved.