【转载】关于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,  true true );
                }
                
else
                {
                    pgmType 
=  Type.GetType(className,  true true );
                }

                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, 
null null null , cultureInfo);
                                
this .output  =   " STo call a method without return value successful. " ;
                            }
                            
else
                            {
                                
this .output  =  (String)pgmType.InvokeMember(programName, defaultBinding,  null null null , cultureInfo);
                            }
                        }
                        
else
                        {
                            
if  (methisInfo.ReturnType  ==   typeof ( void ))
                            {
                                pgmType.InvokeMember(programName, defaultBinding, 
null null , args, cultureInfo);
                                
this .output  =   " STo call a method without return value successful. " ;
                            }
                            
else
                            {
                                
this .output  =  (String)pgmType.InvokeMember(programName, defaultBinding,  null null , 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,  null null null , 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(); 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值