今回は、指定フォルダ配下にある、指定I/Fを実装したアセンブリを、動的に読み込んでインスタンスを生成する実装を行います。
目次
- ソリューション構成
- 取り決めごとにする インターフェース の作成
- プラグイン実態(dllファイル) の 実装
- プラグイン を読み込んで インスタンス の生成
サンプルコード
ソリューション構成
プロジェクトは以下に示す3つを作ります。
- MainWindow
dll動的読み込みをテストする実行可能ファイル。WPFとかWindowsFormsApplicationとか…。IPlugin プロジェクト を参照します。 - IPlugin
取り決めごとにするインターフェース。クラスライブラリ。MainWindow と SamplePlugin から参照される。 - SamplePlugin
プラグインの実態。クラスライブラリ。IPlugin プロジェクト を参照します。
取り決めごとにする インターフェース の作成
IPlugin.cs
1 2 3 4 5 6 7 8 9 10 11 12 | namespace Sample { using System; using System.Collections.Generic; using System.Linq; using System.Text; public interface IPlugin { string Message(); } } |
インターフェースは Message
を返すだけの単純なものを設定します。
必要であればこの インターフェース を追加、修正していきます。
プラグイン実態(dllファイル) の 実装
SamplePlugin.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | namespace Sample { using System; using System.Collections.Generic; using System.Linq; using System.Text; public class Class1 : IPlugin { public string Message() { return "こんちわ" ; } } } |
インターフェース を実装しただけです。
プラグイン を読み込んで インスタンス の生成
MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | namespace Sample { using System.IO; using System.Reflection; using System.Windows; public partial class MainWindow : Window { /// <summary> /// 実行ボタンを押下したとき呼び出されます。 /// </summary> /// <param name="sender">イベント発生元</param> /// <param name="e">イベントオブジェクト</param> private void btnExecute_Click( object sender, RoutedEventArgs e) { var interfaceName = typeof (IPlugin).FullName; var typeName = string .Empty; IPlugin plugin = null ; // プラグインファイルパス一覧の取得 var pluginFilePathList = this .GetPluginFilePathList(); // プラグインからインスタンス生成 foreach (var pluginFilePath in pluginFilePathList) { plugin = this .CreateInstance<IPlugin>(pluginFilePath); if (plugin == null ) { continue ; } this .textBox_filename.Text += plugin.Message(); } } /// <summary> /// プラグインファイルパス一覧を取得します。 /// </summary> /// <returns>プラグインへのファイルパス一覧</returns> private string [] GetPluginFilePathList() { var entryDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var pluginDirectory = @"plugin" ; var targetDirectory = Path.Combine(entryDirectory, pluginDirectory); if (!Directory.Exists(targetDirectory)) { return new string [] { }; } return Directory.GetFiles(targetDirectory, "*.dll" , SearchOption.AllDirectories); } /// <summary> /// 指定された dll のインスタンスを生成します。 /// </summary> /// <typeparam name="T">インターフェース クラス</typeparam> /// <param name="filepath">プラグイン ファイルパス</param> /// <returns>プラグイン から生成 されたインスタンス</returns> private T CreateInstance<T>( string filepath) where T : class { Assembly assembly = null ; string typeName = string .Empty; string interfaceName = typeof (T).FullName; assembly = Assembly.LoadFrom(filepath); if (assembly == null ) { return null ; } foreach (var type in assembly.GetTypes()) { if (type.IsClass && type.IsPublic && !type.IsAbstract && type.GetInterface(interfaceName) != null ) { typeName = type.FullName; break ; } } return assembly.CreateInstance(typeName) as T; } } } |
- btnExecute_Click(object, RoutedEventArgs) : void
エントリーポイント。 ボタンが押されたときに dll を読み込んでインスタンス生成を始めます。
- GetPluginFilePathList() : string[]
プラグイン dll ファイルパス一覧 を取得します。
実行可能ファイルから見て、"plugin" の相対位置にあるフォルダ配下に含まれる拡張子 "*.dll" を探し出します。
実行可能ファイルの場所は Assembly.GetEntryAssembly().Location
で取得します。
指定フォルダ以下のファイル検索は Directory.GetFiles()
で取得します。
- CreateInstance<T>(string) : T
プラグイン dll への絶対パスを引数にとって インスタンス を生成します。
指定されたインターフェースを実装したクラスのインスタンスを生成して返します。
アセンブリ は Assembly.LoadFrom()
で読み込み、 assembly.CreateInstance()
で インスタンス生成 します。
try-catch を行っていないので、適宜追加実装してください。。
おまけ
以下のような ビルド イベント を登録しておくと、コピーしなくて済むので便利です。
TARGET_PRJ
の値は任意に書き換えてください。
1 2 3 4 5 6 7 8 9 10 | set MODE=$(ConfigurationName) set SOLUTION_DIR=$(SolutionDir) set SOURCE_PRJ=$(ProjectName) set TARGET_PRJ=Application set PLUGIN_DIR=plugin set SOURCE_DIR=%SOLUTION_DIR%%SOURCE_PRJ%\bin\%MODE%\*.dll set TARGET_DIR=%SOLUTION_DIR%%TARGET_PRJ%\bin\%MODE%\%PLUGIN_DIR% xcopy "%SOURCE_DIR%" "%TARGET_DIR%" /e /c /i /r /y /f |
今回、以下のサイトを参考にしました。
- MSDN - Assembly クラス
- MSDN - Assembly.CreateInstance メソッド (String)
- MSDN - Assembly.Load メソッド (String)
- MSDN - Directory クラス
- MSDN - Directory.GetFiles メソッド (String, String, SearchOption)
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!