zoukankan      html  css  js  c++  java
  • 【转载】关于C#中动态加载AppDomain的问题

    关于C#中动态加载AppDomain的问题

          在操作系统中,利用进程可以对正在运行的应用程序进行隔离,每个应用程序被加载到单独的进程中,并为其分配虚拟内存,进程无法直接访问物理内存,只能通过操作系统将虚拟内存映射到物理内存中,并保证进程之间的物理内存不会重叠,但是进程最大的缺点就是效率问题,尤其是进程的切换开销很大,而进程间不能共享内存,所以不可能从一个进程通过传递指针给另一个进程。

          在.NET中出现了一个新的概念:AppDomain——应用程序域,所有.NET应用程序都需要运行在托管环境中,操作系统能提供的只有进程,因此.NET程序需要通过AppDomain这个媒介来运行在进程中,同时使用该incheng提供的内存空间,只要是.NET的应用都会运行在某个AppDomain中。 

           当我们运行一个.NET应用程序或者运行库宿主时,OS会首先建立一个进程,然后会在进程中加载CLR(这个加载一般是通过调用_CorExeMain或者_CorBindToRuntimeEx方法来实现),在加载CLR时会创建一个默认的AppDomain,它是CLR的运行单元,程序的Main方法就是在这里执行,这个默认的AppDomain是唯一且不能被卸载的,当该进程消灭时,默认AppDomain才会随之消失。

          一个进程中可以有多个AppDomain,且它们直接是相互隔离的,我们的Assembly是不能单独执行的,它必须被加载到某个AppDomain中,要想卸载一个Assembly就只能卸载其AppDomain。

          最近在我所参加的一个项目中要实现这样一个模块:定制一个作业管理器,它可以定时的以不同频率执行某些.Net应用程序或者存储过程,这里的频率可以是仅一次、每天、每周还是每月进行执行计划的实施,对于调用存储过程没什么好说的,但是调用.Net应用程序的时候就需要考虑如下问题:

          一旦Assembly被作业管理器的服务器调用,(比如某个执行计划正好要被执行了),在调用之前会将程序集加载到默认AppDomain,然后执行,这就有个问题,如果我需要做替换或者删除Assembly等这些操作的时候,由于Assembly已经被默认AppDomain加载,那么对它的更改肯定是不允许的,它会弹出这样的错误: ,除非你关掉作业管理服务器,然后再操作,显然这样做是很不合理的。

          并且默认AppDomain是不能被卸载的,那么我们该怎么办呢,我想到的方法是动态的加载Assembly,新建一个AppDomain,让Assembly加载到这个新AppDomain中然后执行,当执行完后卸载这个新的AppDomain即可,方法如下:

    1、创建程序集加载类AssemblyDynamicLoader,该类用来创建新的AppDomain,并生成用来执行.Net程序的RemoteLoader类:

     using System;

        using System.Collections.Generic;
        
    using System.Globalization;
        
    using System.IO;
        
    using System.Reflection;
        
    using System.Text;
        
    using Ark.Log;

        
    /// <summary>
        
    /// The local loader.
        
    /// </summary>
        public class AssemblyDynamicLoader
        {
            
    /// <summary>
            
    /// The log util.
            
    /// </summary>
            private static ILog log = LogManager.GetLogger(typeof(AssemblyDynamicLoader));

            
    /// <summary>
            
    /// The new appdomain.
            
    /// </summary>
            private AppDomain appDomain;

            
    /// <summary>
            
    /// The remote loader.
            
    /// </summary>
            private RemoteLoader remoteLoader;

            
    /// <summary>
            
    /// Initializes a new instance of the <see cref="LocalLoader"/> class.
            
    /// </summary>
            public AssemblyDynamicLoader()
            {
                AppDomainSetup setup 
    = new AppDomainSetup();
                setup.ApplicationName 
    = "ApplicationLoader";
                setup.ApplicationBase 
    = AppDomain.CurrentDomain.BaseDirectory;
                setup.PrivateBinPath 
    = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
                setup.CachePath 
    = setup.ApplicationBase;
                setup.ShadowCopyFiles 
    = "true";
                setup.ShadowCopyDirectories 
    = setup.ApplicationBase;

                
    this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain"null, setup);
                String name 
    = Assembly.GetExecutingAssembly().GetName().FullName;

                
    this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);
            }

            
    /// <summary>
            
    /// Invokes the method.
            
    /// </summary>
            
    /// <param name="fullName">The full name.</param>
            
    /// <param name="className">Name of the class.</param>
            
    /// <param name="argsInput">The args input.</param>
            
    /// <param name="programName">Name of the program.</param>
            
    /// <returns>The output of excuting.</returns>
            public String InvokeMethod(String fullName, String className, String argsInput, String programName)
            {
                
    this.remoteLoader.InvokeMethod(fullName, className, argsInput, programName);
                
    return this.remoteLoader.Output;
            }

            
    /// <summary>
            
    /// Unloads this instance.
            
    /// </summary>
            public void Unload()
            {
                
    try
                {
                    AppDomain.Unload(
    this.appDomain);
                    
    this.appDomain = null;
                }
                
    catch (CannotUnloadAppDomainException ex)
                {
                    log.Error(
    "To unload assembly error!", ex);
                }
            }
        }

     2、创建RemoteLoader类,它可以在AppDomain中自由穿越,这就需要继承System.MarshalByRefObject这个抽象类,这里RemoteLoader如果不继承MarshalByRefObject类则一定会报错(在不同AppDomain间传递对象,该对象必须是可序列化的)。以RemoteLoader类做为代理来调用待执行的.Net程序。

    using System;
        
    using System.Collections.Generic;
        
    using System.Globalization;
        
    using System.IO;
        
    using System.Reflection;
        
    using System.Text;

        
    /// <summary>
        
    /// The Remote loader.
        
    /// </summary>
        public class RemoteLoader : MarshalByRefObject
        {
            
    /// <summary>
            
    /// The assembly we need.
            
    /// </summary>
            private Assembly assembly = null;

            
    /// <summary>
            
    /// The output.
            
    /// </summary>
            private String output = String.Empty;

            
    /// <summary>
            
    /// Gets the output.
            
    /// </summary>
            
    /// <value>The output.</value>
            public String Output
            {
                
    get
                {
                    
    return this.output;
                }
            }

            
    /// <summary>
            
    /// Invokes the method.
            
    /// </summary>
            
    /// <param name="fullName">The full name.</param>
            
    /// <param name="className">Name of the class.</param>
            
    /// <param name="argsInput">The args input.</param>
            
    /// <param name="programName">Name of the program.</param>
            public void InvokeMethod(String fullName, String className, String argsInput, String programName)
            {
                
    this.assembly = null;
                
    this.output = String.Empty;

                
    try
                {
                    
    this.assembly = Assembly.LoadFrom(fullName);

                    Type pgmType 
    = null;
                    
    if (this.assembly != null)
                    {
                        pgmType 
    = this.assembly.GetType(className, truetrue);
                    }
                    
    else
                    {
                        pgmType 
    = Type.GetType(className, truetrue);
                    }

                    Object[] args 
    = RunJob.GetArgs(argsInput);

                    BindingFlags defaultBinding 
    = BindingFlags.DeclaredOnly | BindingFlags.Public
                            
    | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase
                            
    | BindingFlags.InvokeMethod | BindingFlags.Static;

                    CultureInfo cultureInfo 
    = new CultureInfo("es-ES"false);

                    
    try
                    {
                        MethodInfo methisInfo 
    = RunJob.GetItsMethodInfo(pgmType, defaultBinding, programName);
                        
    if (methisInfo == null)
                        {
                            
    this.output = "EMethod does not exist!";
                        }

                        
    if (methisInfo.IsStatic)
                        {
                            
    if (methisInfo.GetParameters().Length == 0)
                            {
                                
    if (methisInfo.ReturnType == typeof(void))
                                {
                                    pgmType.InvokeMember(programName, defaultBinding, 
    nullnullnull, cultureInfo);
                                    
    this.output = "STo call a method without return value successful.";
                                }
                                
    else
                                {
                                    
    this.output = (String)pgmType.InvokeMember(programName, defaultBinding, nullnullnull, cultureInfo);
                                }
                            }
                            
    else
                            {
                                
    if (methisInfo.ReturnType == typeof(void))
                                {
                                    pgmType.InvokeMember(programName, defaultBinding, 
    nullnull, args, cultureInfo);
                                    
    this.output = "STo call a method without return value successful.";
                                }
                                
    else
                                {
                                    
    this.output = (String)pgmType.InvokeMember(programName, defaultBinding, nullnull, args, cultureInfo);
                                }
                            }
                        }
                        
    else
                        {
                            
    if (methisInfo.GetParameters().Length == 0)
                            {
                                
    object pgmClass = Activator.CreateInstance(pgmType);

                                
    if (methisInfo.ReturnType == typeof(void))
                                {
                                    pgmType.InvokeMember(programName, defaultBinding, 
    null, pgmClass, null, cultureInfo);
                                    
    this.output = "STo call a method without return value successful.";
                                }
                                
    else
                                {
                                    
    this.output = (String)pgmType.InvokeMember(programName, defaultBinding, null, pgmClass, null, cultureInfo);   //'ymtpgm' is program's name and the return value of it must be started with 'O'.
                                }
                            }
                            
    else
                            {
                                
    object pgmClass = Activator.CreateInstance(pgmType);

                                
    if (methisInfo.ReturnType == typeof(void))
                                {
                                    pgmType.InvokeMember(programName, defaultBinding, 
    null, pgmClass, args, cultureInfo);
                                    
    this.output = "STo call a method without return value successful.";
                                }
                                
    else
                                {
                                    
    this.output = (String)pgmType.InvokeMember(programName, defaultBinding, null, pgmClass, args, cultureInfo);   //'ymtpgm' is program's name and the return value of it must be started with 'O'.
                                }
                            }
                        }
                    }
                    
    catch
                    {
                        
    this.output = (String)pgmType.InvokeMember(programName, defaultBinding, nullnullnull, cultureInfo);
                    }
                }
                
    catch (Exception e)
                {
                    
    this.output = "E" + e.Message;
                }
            }

        } 

          其中的InvokeMethod方法只要提供Assembly的全名、类的全名、待执行方法的输入参数和其全名就可以执行该方法,该方法可以是带参数或不带参数,静态的或者不是静态的。

          最后这样使用这两个类:

    AssemblyDynamicLoader loader = new AssemblyDynamicLoader();
    String output 
    = loader.InvokeMethod("fileName""ymtcla""yjoinp""ymtpgm");

     loader.Unload(); 

  • 相关阅读:
    解决SharePoint 文档库itemadded eventhandler导致的上传完成后,编辑页面保持报错的问题,错误信息为“该文档已经被编辑过 the file has been modified by...”
    解决SharePoint 2013 designer workflow 在发布的报错“负载平衡没有设置”The workflow files were saved but cannot be run.
    随机实例,随机值
    Spring4笔记
    struts2笔记(3)
    struts2笔记(2)
    获取文本的编码类型(from logparse)
    FileUtil(from logparser)
    DateUtil(SimpleDateFormat)
    struts2笔记
  • 原文地址:https://www.cnblogs.com/fx2008/p/2267388.html
Copyright © 2011-2022 走看看