プラグインシステムの例

プログラム用のプラグイン(アドイン)のシステムを開発する方法などのタスクを思いつきました。 .NET 3.5以降に提供された少しのアドインを掘り下げてみたところ、それらは私には合わないことがわかりました。 まず、これは便利なディレクトリの場所ではなく、余分なものです。 第二に、プロセスの任意の部分に潜入して、独自の方法で実行することはできません。 拡張性が低い。

そして、彼は自転車を発明することにしました...



メインアプリケーションからの隔離と、サンドボックスの可能性に基づいています。 これはすべて、新しいAppDomainを作成し、InternetZoneで構成することにより、難しくありません。 しかし、開発中に、プラグインを含むアセンブリをドメインにロードすると問題が発生しました。 問題は通信エラーでした。 ロードにはAppDomain.Load()を使用します。 後で判明したように、この方法は私には適していません。 したがって、大きな松葉杖を作る必要はありませんでした。



新しいプロジェクトとコンソールアプリケーション(PluginHostという名前)を作成し、別のクラスライブラリプロジェクトを追加します(PluginFrameworkと呼びましょう)。 クラスをライブラリに追加します。

public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  1. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  2. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  3. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  4. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  5. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



  6. public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .



public abstract class Plugin: MarshalByRefObject { public virtual void Initialize() { } } * This source code was highlighted with Source Code Highlighter .





そして、もう1つのクラスがあります。これがないと、必要なロジックを実装するのが困難でした。





  1. パブリック クラス AssemblyHelper:MarshalByRefObject
  2. {
  3. private AppDomain _currentDomain;
  4. 公開 AssemblyHelper()
  5. {
  6. _currentDomain = AppDomain.CurrentDomain;
  7. _currentDomain.AssemblyResolve + = 新しい ResolveEventHandler(_currentDomain_AssemblyResolve);
  8. }
  9. Assembly _currentDomain_AssemblyResolve( オブジェクト送信者、ResolveEventArgs e)
  10. {
  11. string [] nameSplit = e.Name.Split( '、' );
  12. string path = Path.Combine(SearchFolder、nameSplit [0] + ".dll" );
  13. アセンブリがロードされましたAssembly。
  14. 試してみる
  15. {
  16. loadedAssembly = Assembly .LoadFile(パス);
  17. }
  18. catch (例外exc)
  19. {
  20. 例外exp = exc;
  21. 投げる
  22. }
  23. if (loadedAssembly!= null
  24. {
  25. loadedAssemblyを返します。
  26. }
  27. 他に
  28. {
  29. nullを 返し ます
  30. }
  31. }
  32. パブリック ストリング SearchFolder { get ; セット ; }
  33. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


ベースフレームの準備ができました。 私が実際に達成したかったものの大きな余談ではありません:

1.もう一度メインアプリケーションから分離

2.いつでも、プラグインをロードおよびアンロードします。 たとえば、プラグインがクラッシュした場合、単にそれをアンロードしてリロードします



メインにあるもの:

開始するには、リフレクションのみのモードでPluginFrameworkアセンブリを読み込み、すべてのプラグインの基本クラスのタイプを取得します。





  1. Assembly frameworkReflect = Assembly .ReflectionOnlyLoadFrom(Path.Combine(Environment.CurrentDirectory、 "PluginFramework.dll" ));
  2. タイプbasePluginType = frameworkReflect.GetType( "MyPluginSample.Framework.Plugin" );
*このソースコードは、 ソースコードハイライターで強調表示されました。


次に、使用可能なすべてのフォルダーを検索します





  1. string pathPlugins = Path.Combine(Environment.CurrentDirectory、 "Plugins" );
  2. DirectoryInfo directoryPlugins = 新しい DirectoryInfo(pathPlugins);
  3. foreach (directoryPlugins.GetDirectoriesのvar dir())
  4. {
  5. FileInfo dllFile = dir.GetFiles(dir.Name + ".dll" ).First();
*このソースコードは、 ソースコードハイライターで強調表示されました。


繰り返しますが、リフレクションのみのモードでアセンブリを読み込みますが、プラグインを使用して、プラグインの基本クラスを継承するクラスを探します。 ここでプラグインを使用してアセンブリをロードし、ベースタイプと比較できるように、反映のためにのみPluginFrameworkの最初にロードする必要がありました。





  1. アセンブリ reflectionAsm = Assembly .ReflectionOnlyLoadFrom(dllFile.FullName);
  2. タイプtypePlugin = reflectionAsm.GetTypes()。First(t => t.IsSubclassOf(basePluginType));
*このソースコードは、 ソースコードハイライターで強調表示されました。


これで、新しいドメインの作成を開始し、必要なものをすべてロードして、プラグインを実行して実行できます





  1. AppDomain pluginDomain = AppDomain.CreateDomain(dir.Name + "plugin" );
  2. AssemblyHelperヘルパー=(AssemblyHelper)pluginDomain.CreateInstanceAndUnwrap( "PluginFramework""MyPluginSample.Framework.AssemblyHelper" );
  3. helper.SearchFolder = dir.FullName;
  4. プラグインplugin =(プラグイン)pluginDomain.CreateInstanceAndUnwrap(reflectionAsm.FullName、typePlugin.FullName);
  5. plugin.Initialize();
*このソースコードは、 ソースコードハイライターで強調表示されました。


以上です!

そして、テストするテストプラグインを作成します





  1. パブリック クラス MyPlugin1:プラグイン
  2. {
  3. public override void Initialize()
  4. {
  5. Console .WriteLine( 「プラグインで実行1.ドメインID:{0}」 、AppDomain.CurrentDomain.Id);
  6. }
  7. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


ディレクトリ構造は次のとおりです





ソースPluginSystem.zipへのリンク



PSアセンブリをリフレクション用にロードしています。アセンブリを新しいドメインに単純にアップロードすると、子ドメインからアセンブリを通過して必要なクラスを見つけることができないためです。 CLRはこのアセンブリを子ドメインにロードしようとします



All Articles