- PR -

別AppDomainへのアセンブリ読み込み

1
投稿者投稿内容
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2006-05-22 00:07
[NET2.0]

プラグインの動的読込・解放を目的とし、
AppDomain を使ったアセンブリロードを試みています。

作成した AppDomain インスタンスから Load メソッドを呼び出し、
アセンブリを読み込むことはできるのですが、
結局 CurrentDomain にも読み込まれてしまいます。

 AppDomain domain = ドメイン作成;

 // これを実行すると CurrentDomain にも読み込まれてしまいます
 // ファイルは CurrentDomain からも解決できる位置に格納されています
 Assembly asm = domain.Load(new AssemblyName(アセンブリファイル));

確かに MSDN にも「読み込まれたアセンブリは実行中のドメインにも読み込まれます」
という記述はあります。

プラグインなどのアセンブリを動的に取得し使用後に解放するには
AppDomain を使って読み込んで AppDomain ごとアンロードする、
という方法があるといろいろなサイトでも紹介されていますが、
それは対象アセンブリ内に定義されている型のインスタンスを生成して
プロキシとして返す場合に限られるのでしょうか。
(UnWrap を使えばアセンブリは読み込まれない、とあります)

なお、アセンブリの読み込みは、
プラグインの型の一覧を取得するために行っています。
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2006-05-22 10:27
プラグインの一覧を作ったり遅延呼出を行うためには
型名と表示名称などの文字列情報を取得できればよいと割り切り、
自アセンブリ内に定義されているプラグイン型の情報を取得して返すクラスを作成することにしました。

共通インターフェースアセンブリ

namespace Plugins {
 // プラグイン一覧の取得を行うインターフェースと基底クラス
 public interface IPluginLoader {
 }
 public abstract class PluginLoaderBase : IPluginLoader {
  // 自アセンブリ内の型を取得して返すメソッドを実装
 }
 // プラグイン情報
 public class PluginInfo {
 }
 // プラグインが実装するインターフェース
 public interface IPlugin {
 }
}

プラグインアセンブリ

namespace PluginSample {
 // [アセンブリ名].PluginLoader という名前で定義するルールとする
 public class PluginLoader : Plugins.PluginLoaderBase {
  // 取得メソッド自体は基底クラスで定義済み
 }
 public class Plugin1 : Plugins.IPlugin {
 }
}

アプリケーション

// AppDomain作成
AppDomain domain =
 AppDomain.CreateDomain(_tempDomainName, null, null);

// ローダーインスタンス作成
object obj =
 domain.CreateInstanceAndUnwrap("PluginSample", "PluginSample.PluginLoader");

// クラスにキャストするとカレントドメインにもアセンブリがロードされるので、
// インターフェースにキャスト
// Plugins.PluginLoaderBase loader = obj as Plugins.PluginLoaderBase;
Plugins.IPluginLoader loader = obj as Plugins.IPluginLoader;

// 一覧取得
Plugins.PluginInfo[] plugins = loader.FindPlugins();

domain.Unload();


このようにすると、
カレントドメインには PluginSample アセンブリが読み込まれないようです。

ただ、CreateInstanceAndUnwrap で PluginLoader を取得する際、
[アセンブリ名].PluginLoader であるとして型名を決めうちしています。
これを自動判定できればさらにスマートなのですが。

なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2006-05-23 00:22
共通インターフェースアセンブリに、MarshalByRefObject派生のユーティリティクラスを追加し、
アセンブリ名を受け取ってそのアセンブリをロードし、型(名)のリストを返すメソッドを実装。

呼び出し側AppDomainで新しいAppDomainを作成したら、CreateInstanceAndUnwrapで
上記のMarshalByRefObject派生のユーティリティクラスのインスタンスを作成。
対象プラグインのアセンブリ名を指定してメソッドを実行。

これで、現在のドメインにアセンブリをロードせずに、対象アセンブリに含まれる
型名一覧等を取得できます。
※プラグインのアセンブリに特別なクラスを追加する必要はありません。

まあ要するに先の投稿で書かれているPluginLoader自体を、
共通インターフェースアセンブリ内に実装してしまうイメージです。

--追記
って思いましたけど、先の投稿でも実質共通インターフェイスの方で実装されてますね。
わざわざ各アセンブリの方で実装クラスを作ってるのはなんででしょう?

[ メッセージ編集済み 編集者: なちゃ 編集日時 2006-05-23 01:16 ]
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2006-05-23 10:04
なちゃさん、ご意見ありがとうございます。

> って思いましたけど、先の投稿でも実質共通インターフェイスの方で実装されてますね。
> わざわざ各アセンブリの方で実装クラスを作ってるのはなんででしょう?

まったくそうですよね。

型の一覧を取得しようとするとだめということに気がついて一晩寝たら

「型の一覧をプラグインのアセンブリ内で取得させればいいじゃん」

ってひらめいた(笑)ところで思考停止しました。
プラグインの型の一覧を取得するくらいはたいしたロジックではありませんが、
都度記述するのもどうかなと思い、抽象クラスを用意したというわけです。


取得処理を別ドメインで行えばよいわけで、
取得処理自体がプラグインアセンブリになくてはならない必要なんてないですよね。

1

スキルアップ/キャリアアップ(JOB@IT)