1. ホーム
  2. cp

AppDomainとAssemblyの動的なロードとアンロード

2022-02-20 21:38:53

この問題をわかりやすく説明するために、例を見てみましょう。この例では、WinForm 上にボタンがあり、ユーザーがそれをクリックすると、既存のアセンブリがロードされ、アセンブリの FullName がインターフェイス内の Label コントロールに表示されます。以下のコードのように、Assembly.LoadFile メソッドを実行し、FullName プロパティを使用して表示します。

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     Assembly assembly = Assembly.LoadFile(@"C:\testlib.dll");  
  4.     label1.Text = assembly.FullName;  
  5. }  

もちろん、プログラムは正常に実行され、コンパイル時や実行時のエラーは見当たりません。しかし、このプログラムを終了せずに、testlib.dllという名前のコンパイルに戻ると、Visual Studioはコンパイルを完了できないことがわかります。これは、このファイルが他のプロセスで使用されていることを示唆しています。

実は、今回のプログラムはこのtestlib.dllとはあまり関係がなく、testlib.dllの基本情報を表示するだけのプログラムです。もしtestlib.dllが共有ライブラリであれば、リソースの排他性の問題は他のプログラムにも影響します。

AssemblyにはUnloadの機能がありませんが、AppDomainを使うことでこの問題を解決することができます。基本的な考え方は、新しいAppDomainを作成し、新しいAppDomainにアセンブリをロードし、その中のメソッドを呼び出し、その結果を返すというものです。注意:ロードしたアセンブリを、現在のアプリケーションドメイン(AppDomain)に直接返すことはできません。

まず、新しい AppDomain にアセンブリをロードする RemoteLoader を作成し、アセンブリの FullName が利用できるように、プロパティを外部にアドバタイズします。そのコードは以下の通りです。

  1. public class RemoteLoader : MarshalByRefObject  
  2. {  
  3.     private Assembly アセンブリ。  
  4.     public void LoadAssembly(string fullName)  
  5.     {  
  6.         assembly = Assembly.LoadFrom(fullName)。  
  7.     }  
  8.     public string FullName  
  9.     {  
  10.         get { return assembly.FullName; }.  
  11.     }  
  12. }  

次に、LocalLoaderを作成します。この機能は、新しいAppDomainを作成し、この新しいAppDomainでRemoteLoaderを呼び出し、RemoteLoaderを通してアセンブリを作成し、アセンブリの情報を取得することです。このとき、呼び出されたアセンブリは当然、新しいAppDomainにロードされる。最後に、LocalLoaderもAppDomainをアンロードするための新しいメソッドを提供する必要があります。そのコードは以下の通りです。

  1. public class LocalLoader  
  2. {  
  3.     private AppDomain appDomain;  
  4.     private RemoteLoader remoteLoader;  
  5.     public LocalLoader()  
  6.     {  
  7.         AppDomainSetup setup = new AppDomainSetup()。  
  8.         setup.ApplicationName = "Test"。  
  9.         setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;  
  10.         setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private") とします。  
  11.         setup.CachePath = setup.ApplicationBase.CachePath。  
  12.         setup.ShadowCopyFiles = "true";  
  13.         setup.ShadowCopyDirectories = setup.ApplicationBase.Setup.ShadowCopyDirectories。  
  14.         appDomain = AppDomain.CreateDomain("TestDomain", null, setup);  
  15.         文字列名 = Assembly.GetExecutingAssembly().GetName().FullName.FullName.FullName.FullName;  
  16.         remoteLoader = (RemoteLoader)appDomain.CreateInstanceAndUnwrap()  
  17.             という名前を付けます。  
  18.             typeof(RemoteLoader).FullName)を使用します。  
  19.     }  
  20.     public void LoadAssembly(string fullName)  
  21.     {  
  22.         remoteLoader.LoadAssembly(fullName)を実行します。  
  23.     }  
  24.     public void Unload()  
  25.     {  
  26.         AppDomain.Unload(appDomain)を実行します。  
  27.         appDomain = nullとする。  
  28.     }  
  29.     public string FullName  
  30.     {  
  31.         得る  
  32.         {  
  33.             remoteLoader.FullNameを返します。  
  34.         }  
  35.     }  
  36. }  

最後に、WinFormのButton Clickイベントハンドラを以下のような形に変更します。

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     LocalLoader ll = new LocalLoader()。  
  4.     ll.LoadAssembly(@"C:\testlib.dll") を実行します。  
  5.     label1.Text = ll.FullName;  
  6.     ll.Unload()を実行します。  
  7. }  

上記の変更を行った後、当プログラムはアセンブリのFullNameも正しく表示し、アセンブリ情報を表示した後、testlib.dllのリソース排他性が他のプログラムに影響しないよう、新しく作成したAppDomainを積極的にアンロードしています。

参考 http://www.microsoft.com/china/msdn/library/langtool/vcsharp/csharp05162002.mspx?mfr=true