熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

關於C#中動態加載AppDomain的問題

2022-06-13   來源: .NET編程 

  在操作系統中利用進程可以對正在運行的應用程序進行隔離每個應用程序被加載到單獨的進程中並為其分配虛擬內存進程無法直接訪問物理內存只能通過操作系統將虛擬內存映射到物理內存中並保證進程之間的物理內存不會重疊但是進程最大的缺點就是效率問題尤其是進程的切換開銷很大而進程間不能共享內存所以不可能從一個進程通過傳遞指針給另一個進程
 
  在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即可方法如下
  
  創建程序集加載類AssemblyDynamicLoader該類用來創建新的AppDomain並生成用來執行Net程序的RemoteLoader類:
  
   using System;
  
   using SystemCollectionsGeneric;
   using SystemGlobalization;
   using SystemIO;
   using SystemReflection;
   using SystemText;
   using ArkLog;
  
   /// <summary>
   /// The local loader
   /// </summary>
   public class AssemblyDynamicLoader
   {
   /// <summary>
   /// The log util
   /// </summary>
   private static ILog log = LogManagerGetLogger(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();
   setupApplicationName = ApplicationLoader;
   setupApplicationBase = AppDomainCurrentDomainBaseDirectory;
   setupPrivateBinPath = PathCombine(AppDomainCurrentDomainBaseDirectory private);
   setupCachePath = setupApplicationBase;
   setupShadowCopyFiles = true;
   setupShadowCopyDirectories = setupApplicationBase;
  
   thisappDomain = AppDomainCreateDomain(ApplicationLoaderDomain null setup);
   String name = AssemblyGetExecutingAssembly()GetName()FullName;
  
   thisremoteLoader = (RemoteLoader)thisappDomainCreateInstanceAndUnwrap(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)
   {
   thisremoteLoaderInvokeMethod(fullName className argsInput programName);
   return thisremoteLoaderOutput;
   }
  
   /// <summary>
   /// Unloads this instance
   /// </summary>
   public void Unload()
   {
   try
   {
   AppDomainUnload(thisappDomain);
   thisappDomain = null;
   }
   catch (CannotUnloadAppDomainException ex)
   {
   logError(To unload assembly error! ex);
   }
   }
   }
  
  
   創建RemoteLoader類它可以在AppDomain中自由穿越這就需要繼承SystemMarshalByRefObject這個抽象類這裡RemoteLoader如果不繼承MarshalByRefObject類則一定會報錯

  (在不同AppDomain間傳遞對象該對象必須是可序列化的)以RemoteLoader類做為代理來調用待執行的Net程序
  
  using System;
   using SystemCollectionsGeneric;
   using SystemGlobalization;
   using SystemIO;
   using SystemReflection;
   using SystemText;
  
   /// <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 = StringEmpty;
  
   /// <summary>
   /// Gets the output
   /// </summary>
   /// <value>The output</value>
   public String Output
   {
   get
   {
   return thisoutput;
   }
   }
  
   /// <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)
   {
   thisassembly = null;
   thisoutput = StringEmpty;
  
   try
   {
   thisassembly = AssemblyLoadFrom(fullName);
  
   Type pgmType = null;
   if (thisassembly != null)
   {
   pgmType = thisassemblyGetType(className true true);
   }
   else
   {
   pgmType = TypeGetType(className true true);
   }
  
   Object[] args = RunJobGetArgs(argsInput);
  
   BindingFlags defaultBinding = BindingFlagsDeclaredOnly | BindingFlagsPublic
   | BindingFlagsNonPublic | BindingFlagsInstance | BindingFlagsIgnoreCase
   | BindingFlagsInvokeMethod | BindingFlagsStatic;
  
   CultureInfo cultureInfo = new CultureInfo(esES false);
  
   try
   {
   MethodInfo methisInfo = RunJobGetItsMethodInfo(pgmType defaultBinding programName);
   if (methisInfo == null)
   {
   thisoutput = EMethod does not exist!;
   }
  
   if (methisInfoIsStatic)
   {
   if (methisInfoGetParameters()Length == )
   {
   if (methisInfoReturnType == typeof(void))
   {
   pgmTypeInvokeMember(programName defaultBinding null null null cultureInfo);
   thisoutput = STo call a method without return value successful;
   }
   else
   {
   thisoutput = (String)pgmTypeInvokeMember(programName defaultBinding null null null cultureInfo);
   }
   }
   else
   {
   if (methisInfoReturnType == typeof(void))
   {
   pgmTypeInvokeMember(programName defaultBinding null null args cultureInfo);
   thisoutput = STo call a method without return value successful;
   }
   else
   {
   thisoutput = (String)pgmTypeInvokeMember(programName defaultBinding null null args cultureInfo);
   }
   }
   }
   else
   {
   if (methisInfoGetParameters()Length == )
   {
   object pgmClass = ActivatorCreateInstance(pgmType);
  
   if (methisInfoReturnType == typeof(void))
   {
   pgmTypeInvokeMember(programName defaultBinding null pgmClass null cultureInfo);
   thisoutput = STo call a method without return value successful;
   }
   else
   {
   thisoutput = (String)pgmTypeInvokeMember(programName defaultBinding null pgmClass null cultureInfo); //ymtpgm is programs name and the return value of it must be started with O
   }
   }
   else
   {
   object pgmClass = ActivatorCreateInstance(pgmType);
  
   if (methisInfoReturnType == typeof(void))
   {
   pgmTypeInvokeMember(programName defaultBinding null pgmClass args cultureInfo);
   thisoutput = STo call a method without return value successful;
   }
   else
   {
   thisoutput = (String)pgmTypeInvokeMember(programName defaultBinding null pgmClass args cultureInfo); //ymtpgm is programs name and the return value of it must be started with O
   }
   }
   }
   }
   catch
   {
   thisoutput = (String)pgmTypeInvokeMember(programName defaultBinding null null null cultureInfo);
   }
   }
   catch (Exception e)
   {
   thisoutput = E + eMessage;
   }
   }
   }
  
   其中的InvokeMethod方法只要提供Assembly的全名類的全名待執行方法的輸入參數和

  其全名就可以執行該方法該方法可以是帶參數或不帶參數靜態的或者不是靜態的
  
   最後這樣使用這兩個類
  
  AssemblyDynamicLoader loader = new AssemblyDynamicLoader();
  String output = loaderInvokeMethod(fileName ymtcla yjoinp ymtpgm);
   loaderUnload();  


From:http://tw.wingwit.com/Article/program/net/201311/11489.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.