C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

  AppDomain 表示应用程序域,它是一个应用程序在其中执行的独立环境。每个应用程序只有一个主应用程序域,

但是一个应用程序可以创建多个子应用程序域。

  因此可以通过 AppDomain 创建新的应用程序域,在新创建的子应用程序域中加载执行程序集并且在执行完毕后释放程序集资源,来实现系统在运行中程序集的动态加载或卸载,从而达到系统运行中程序集热更新的目的。

主应用程序入口:

using Kernel.ServiceAgent;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Kernel.App
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("");

            using (ServiceManager<IObjcet> manager = new ServiceManager<IObjcet>())
            {
                string result = manager.Proxy.Put("apprun one");
                Console.WriteLine(result);
                Console.WriteLine("");
                Console.WriteLine("                         Thread AppDomain info                             ");
                Console.WriteLine(manager.CotrProxy.FriendlyName);
                Console.WriteLine(Thread.GetDomain().FriendlyName);
                Console.WriteLine(manager.CotrProxy.BaseDirectory);
                Console.WriteLine(manager.CotrProxy.ShadowCopyFiles);
                Console.WriteLine("");
            }

            Console.ReadLine();
        }
    }
}

创建新的应用程序域并且在新的应用程序域中调用透明代理类:

using Kernel.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Kernel.ServiceAgent
{
    public class ServiceManager<T> : IDisposable where T : class
    {
        private AppDomain ctorProxy = null;

        /// <summary>
        /// 应用程序运行域容器
        /// </summary>
        public AppDomain CotrProxy
        {
            get { return ctorProxy; }
        }

        private T proxy = default(T);

        public T Proxy
        {
            get
            {
                if (proxy == null)
                {
                    proxy = (T)InitProxy(AssemblyPlugs);
                }
                return proxy;
            }
        }

        private string assemblyPlugs;

        /// <summary>
        /// 外挂插件程序集目录路径
        /// </summary>
        public string AssemblyPlugs
        {
            get {
                assemblyPlugs = ConfigHelper.GetVaule("PrivatePath");
                if (assemblyPlugs.Equals("")){
                    assemblyPlugs = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
                }
                if (!Directory.Exists(assemblyPlugs))
                {
                    Directory.CreateDirectory(assemblyPlugs);
                }
                return assemblyPlugs;
            }
            set { assemblyPlugs = value; }
        }

        public ServiceManager()
        {
            if (proxy == null)
            {
                proxy = (T)InitProxy(AssemblyPlugs);
            }
        }

        private T InitProxy(string assemblyPlugs)
        {
            try
            {
                //AppDomain.CurrentDomain.SetShadowCopyFiles();
                //Get and display the friendly name of the default AppDomain.
                //string callingDomainName = Thread.GetDomain().FriendlyName;

                //Get and display the full name of the EXE assembly.
                //string exeAssembly = Assembly.GetEntryAssembly().FullName;
                //Console.WriteLine(exeAssembly);

                AppDomainSetup ads = new AppDomainSetup();
                ads.ApplicationName = "Shadow";

                //应用程序根目录
                ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

                //子目录(相对形式)在AppDomainSetup中加入外部程序集的所在目录,多个目录用分号间隔
                ads.PrivateBinPath = assemblyPlugs;

                ads.CachePath = ads.ApplicationBase;
                ads.ShadowCopyFiles = "true";
                ads.ShadowCopyDirectories = ads.ApplicationBase;

                ads.DisallowBindingRedirects = false;
                ads.DisallowCodeDownload = true;

                ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

                //Create evidence for the new application domain from evidence of
                Evidence adevidence = AppDomain.CurrentDomain.Evidence;

                // Create the second AppDomain.
                ctorProxy = AppDomain.CreateDomain("AD #2", adevidence, ads);

                //Type.GetType("Kernel.TypeLibrary.MarshalByRefType").Assembly.FullName
                string assemblyName = Assembly.GetExecutingAssembly().GetName().FullName;
                //string assemblyName = typeof(MarshalByRefType).Assembly.FullName

                // Create an instance of MarshalByRefObject in the second AppDomain.
                // A proxy to the object is returned.

                Console.WriteLine("CtorProxy:" + Thread.GetDomain().FriendlyName);

                //TransparentFactory factory = (IObjcet)ctorProxy.CreateInstance("Kernel.TypeLibrary",               "Kernel.TypeLibrary.TransparentFactory").Unwrap();
                TransparentAgent factory = (TransparentAgent)ctorProxy.CreateInstanceAndUnwrap(assemblyName,                               typeof(TransparentAgent).FullName);

                Type meetType = typeof(T);
                string typeName = AssemblyHelper.CategoryInfo(meetType);

                object[] args = new object[0];
                string assemblyPath = ctorProxy.SetupInformation.PrivateBinPath;

                //IObjcet ilive = factory.Create(@"E:\Plug\Kernel.SimpleLibrary.dll", "Kernel.SimpleLibrary.PlugPut", args);
                T obj = factory.Create<T>(assemblyPath, typeName, args);

                return obj;
            }
            catch (System.Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 卸载应用程序域
        /// </summary>
        public void Unload()
        {
            try
            {
                if (ctorProxy != null)
                {
                    AppDomain.Unload(ctorProxy);
                    ctorProxy = null;
                }
            }
            catch(Exception)
            {
                throw;
            }
        }

        public void Dispose()
        {
            this.Unload();
        }
    }
}

创建应用程序代理类:

using Kernel.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Kernel.ServiceAgent
{
    public class TransparentAgent : MarshalByRefObject
    {
        private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;

        public TransparentAgent() { }

        /// <summary> Factory method to create an instance of the type whose name is specified,
        /// using the named assembly file and the constructor that best matches the specified parameters. </summary>
        /// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>
        /// <param name="typeName"> The name of the preferred type. </param>
        /// <param name="constructArgs"> An array of arguments that match in number, order,     /// and type the parameters of the constructor to invoke, or null for default constructor. </param>
        /// <returns> The return value is the created object represented as IObjcet. </returns>
        public IObjcet Create(string assemblyFile, string typeName, object[] args)
        {
            return (IObjcet)Activator.CreateInstanceFrom(assemblyFile, typeName, false, bfi, null, args, null, null).Unwrap();
        }

        public T Create<T>(string assemblyPath, string typeName, object[] args)
        {
            string assemblyFile = AssemblyHelper.LoadAssemblyFile(assemblyPath, typeName);
            return (T)Activator.CreateInstanceFrom(assemblyFile, typeName, false, bfi, null, args, null, null).Unwrap();
        }
    }
}

所有涉及到需要动态加载或释放的资源,都需要放在代理类中进行操作,只有在此代理类中进行托管的代码才是属于新建的应用程序域的操作。

using Kernel.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Kernel.ServiceAgent
{
    public class AssemblyHelper
    {
        /// <summary>
        /// 获取泛型类中指定属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static string CategoryInfo(Type meetType)
        {
            object[] attrList = meetType.GetCustomAttributes(typeof(CategoryInfoAttribute), false);
            if (attrList != null)
            {
                CategoryInfoAttribute categoryInfo = (CategoryInfoAttribute)attrList[0];
                return categoryInfo.Category;
            }
            return "";
        }

        public static string LoadAssemblyFile(string assemblyPlugs, string typeName)
        {
            string path = string.Empty;
            DirectoryInfo d = new DirectoryInfo(assemblyPlugs);
            foreach (FileInfo file in d.GetFiles("*.dll"))
            {
                Assembly assembly = Assembly.LoadFile(file.FullName);
                Type type = assembly.GetType(typeName, false);
                if (type != null)
                {
                    path = file.FullName;
                }
            }
            return path;
        }
    }
}

读取配置文件信息:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;

namespace Kernel.ServiceAgent
{
    public class ConfigHelper
    {
        public static string GetVaule(string configName)
        {
            string configVaule = ConfigurationManager.AppSettings[configName];
            if (configVaule != null && configVaule != "")
            {
                return configVaule.ToString();
            }
            return "";
        }
    }
}

配置文件相关配置信息:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <appSettings>
      <add key="PrivatePath" value="E:\Plugins"/>
    </appSettings>
</configuration>

创建接口信息:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kernel.Interface
{
    [CategoryInfo("Kernel.SimpleLibrary.PlugPut", "")]
    public interface IObjcet
    {
        void Put();

        string Put(string plus);
    }
}

创建接口自定义属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kernel.Interface
{
    /// <summary>
    /// 设置接口实现类自定义标注属性
    /// </summary>
    ///
   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
    public class CategoryInfoAttribute : Attribute
    {
        public string Category { get; set; }

        public string Describtion { get; set; }

        /// <summary>
        /// 设置实现类自定义标注属性
        /// </summary>
        /// <param name="category"></param>
        /// <param name="describtion"></param>
        public CategoryInfoAttribute(string category, string describtion)
        {
            this.Category = category;
            this.Describtion = describtion;
        }
    }
}

创建继承至IObjcet接口带有具体操作的实现类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kernel.Interface;

namespace Kernel.SimpleLibrary
{
    [Serializable]
    public class PlugPut : MarshalByRefObject, IObjcet
    {

        private string plugName = "my plugName value is default!";

        public string PlugName
        {
            get { return plugName; }
            set { plugName = value; }
        }

        public PlugPut() { }

        public PlugPut(string plusName)
        {
            this.PlugName = plusName;
        }

        public void Put()
        {
            Console.WriteLine("Default plug value is:" + plugName);
        }

        public string Put(string plus)
        {
            Console.WriteLine("Put plus value is:" + plus);
            return ("-------------------- PlugPut result info is welcome -------------------------");
        }
    }
}

小节:

  如果在另一个AppDomain 中加载程序集,然后获取Type,最后在主AppDomain中使用CreateInstance中的Type重载创建对象,结果会是Type所属的程序集会被加入到当前AppDomain中,然后Type的实例会在当前AppDomain中创建。

  只有继承至 MarshalByRefObject 的透明代理类才能够进行跨域操作。

  所以需要在继承至 MarshalByRefObject 的透明代理类中进行相关操作然后返回给主应用程序域,只有在代理类中进行的代码操作才是属于新建的应用程序域。否则任何运行代理类以外的代码都是属于主应用程序域。

时间: 2024-10-05 04:19:28

C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载的相关文章

[转] 通过反射实现动态功能模块加载

原文 如何通过反射实现动态功能模块加载 程序集包含模块,而模块包含类型,类型又包含成员.反射则提供了封装程序集.模块和类型的对象.您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型.然后,可以调用类型的方法或访问其字段和属性. 下面我们将介绍如何通过不使用反射的方式和使用反射的方式加载功能模块.实现效果: 1. WinForm 主程序 主程序我们使用Winform程序,VS2008 工具C#语言开发.包括工具栏.状态栏及TabControl控件.我们使用TabCon

如何逐步实现动态库的加载,类型的匹配

如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,参考下面宏定义即可:  #define LIBEXPORT_API extern "C" __declspec(dllexport) 第一步,我先从简单的调用出发,定义了一个简单的函数,该函数仅仅实现一个整数加法求和: LIBEXPORT_API int mySum(int a,int b){ return a+b;}  C# 导入定义: public class RefComm  {  [DllImport("

如何通过反射实现动态功能模块加载

程序集包含模块,而模块包含类型,类型又包含成员.反射则提供了封装程序集.模块和类型的对象.您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型.然后,可以调用类型的方法或访问其字段和属性. 下面我们将介绍如何通过不使用反射的方式和使用反射的方式加载功能模块.实现效果: 1.        WinForm 主程序 主程序我们使用Winform程序,VS2008 工具C#语言开发.包括工具栏.状态栏及TabControl控件.我们使用TabControl 作为功能模块容器

[ExtJS5学习笔记]第二十节 Extjs5配合数组的push方法,动态创建并加载组件

本文地址:http://blog.csdn.net/sushengmiyan/article/details/39226773 官方例子:http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Array-method-push 本文作者:sushengmiyan -------------------------------------------------------------------------------------------------

PHP+Mysql+easyui点击左侧tree菜单对应表名右侧动态生成datagrid加载表单数据(二)

关于tree菜单生成,参考我的另一篇博文地址tree 菜单 实现功能:点击左侧tree菜单中的table,右侧通过datagrid加载出该表对用的所有数据 难点:获取该表的所有列名,动态生成datagrid,并加载数据 解决办法: 使用tree菜单的onClick事件: $('#tree').tree( { url:'tree_getData.php', onClick:function(node){ //判断点击的节点是否是子节点是子节点就创建datagrid,否则就return打开这个节点

关于Linux动态库的加载路径

问题 按如下步骤在Ubuntu上编译安装Google Protocol Buffers $ ./configure $ make $ make check $ sudo make install 运行 $ protoc --version 出现找不到动态库的错误 protoc: error while loading shared libraries: libprotobuf.so.7: cannot open shared object file: No such file or direct

VC中实现dll动态库文件加载

例如有动态库:math.h; math.dll; math.lib1.系统路径中包含math.dll路径.2.VC工程中,“项目”>”属性“>”C/C++“>”常规“>”附加包含目录“,加入math.h所在路径.3.VC工程中,“项目”>”属性“>”连接器“>”常规“>”附加库目录“,加入math.lib所在路径.4.VC工程中,“项目”>”属性“>”连接器“>”输入“>”附加依赖项“,加入"math.lib".

PHP获取HTML内容及动态渲染js加载内容

写爬虫的时候,使用guzzle异步并发的get请求真的好用,可以快速爬取,及时PHP不是多线程的,却能使用协程实现异步并发-用户态的多线程,也有时候,请求地址返回的页面很多待执行的JavaScript代码,数据需要动态渲染上去,这里有个简单的方法 就是使用querylist,用了这个扩展也可以不再依赖php的dom解析工具-simpledom,也自带了远程获取功能. 1.安装 安装querylist composer require jaeger/querylist 安装phantomjs co

使用tcp_probe时最初没有输出,先卸载后加载模块之后就有了。

刚才尝试使用tcp_probe来查看tcp的窗口的变化,最初按照tcpprobe | The Linux Foundation的步骤进行设置,但是iperf之后tcp_probe并没有输出结果.按照tcp_probe module does no output中的回答先modprobe -r tcp_probe后加载就好了. google过程中发现对于tcp_probe的使用和源码分析还两个不错的参考材料 mscom_network_start [Digitale Elektronik und