プラグイン処理 2 (DLL/C#/Python に対応させてみる)
前回の「プラグイン処理」の続き。
今回は、前回のコードに少し付け足して、様々な種類のプラグインに対応してみよう。
前回は、DLL だけをプラグインとして使えるようにしたが、今回は、それに加えて、C# と Python のプラグインも使えるようにしてみたい。
■ 今回のプラグインの規約
今回のプラグインも、前回同様、以下のような規約ベースで動くものとする。
- 実行中のプログラムがあるフォルダーの下の Plugin と云う名前のフォルダーにある dll ファイルをプラグインのアセンブリ、cs ファイルを C# のプラグイン、py ファイルを Python のプラグインと看做す。
- DLL プラグインと C# プラグインでは、最初の public なクラスをプラグインと見做す。
- プラグインは、必ず string Name() と云う名称を返す public なメソッドを持っている。
- プラグインは、必ず void Run() と云う public なメソッドによって動作する。
■ プラグイン側の実装の例
では、プラグイン側から実装してみよう。
今回用意するのは、以下の三種類だ。
・DLL プラグインの実装の例
DLL プラグインは、クラスライブラリとして作成し、ビルドして "Test.dll" とする。
public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。
// DLL プラグイン (クラスライブラリとして作成し、ビルドして "Test.dll" に) using System; public class Plugin { public string Name() { return "DLL Plugin"; } public void Run() { Console.WriteLine("DLL Plugin is running!"); } }
・C# プラグインの実装の例
C# プラグインは、一つの cs ファイルだ。"Test.cs" として保存する。
public なクラスを持ち、その中には、public な Name() メソッドと public な Run() メソッドを持つ。
// C# プラグイン ("Test.cs" として保存) using System; public class Plugin { public string Name() { return "C# Plugin"; } public void Run() { Console.WriteLine("C# Plugin is running!"); } }
・Python プラグインの実装の例
Python プラグインは、一つの py ファイルだ。"Test.py" として保存する。
Name() メソッドと Run() メソッドを持つ。
# Python Plugnin (Save as "Test.py".) def Name(): return "Python plugin" def Run(): print "Python plugin is running!\n"
■ プラグインが組み込まれる本体側の実装の例
次に、プラグインが組み込まれる本体側の実装だ。
・IronPython を利用する為の準備
先ず、Python をプラグインとして使えるようにするために、IronPython をインストールしよう。
IronPython は、Visual Studio で NuGet からインストール出来る。
IronPython のインストールが終わると、プロジェクトの参照設定は、次のように IronPython を使う為の参照が追加されている。
・プラグインが組み込まれる本体側の実装の例
では、本体側を実装しよう。
using IronPython.Hosting; // Python プラグインの処理に必要 using Microsoft.CSharp; // C# プラグインの処理に必要 using System; using System.CodeDom.Compiler; // C# プラグインの処理に必要 using System.IO; using System.Linq; using System.Reflection; class Program { // プラグインのフォルダー名 const string pluginFolderName = "Plugin"; static void Main() { // プラグインのフォルダーへのフルパス名を取得し、 var pluginFolderName = GetPluginFolderName(); // もしプラグインのフォルダーが在ったら、 if (Directory.Exists(pluginFolderName)) // プラグインのフォルダーの各のプラグインのパス名を使って、プラグインを実行 Directory.GetFiles(pluginFolderName, "*.*").ToList().ForEach(Run); } // プラグインのフォルダーへのフルパス名を取得 static string GetPluginFolderName() { // 実行中のこのプログラム自体のアセンブリのパスのフォルダーの下の Plugin フォルダーへのフルパスを取得 return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + pluginFolderName; } // プラグインを実行 static void Run(string pluginPath) { switch (Path.GetExtension(pluginPath).ToLower()) { case ".dll": /* DLL の場合 */ RunDll(pluginPath); break; case ".cs" : /* C# のコードの場合 */ RunCSharp(pluginPath); break; case ".py": /* Python のコードの場合 */ RunPython(pluginPath); break; } } // DLL プラグインを実行 static void RunDll(string path) { // DLL をアセンブリとして読み込む var assembly = Assembly.LoadFrom(path); if (assembly != null) // アセンブリをプラグインとして実行 Run(assembly); } // C# プラグインを実行 static void RunCSharp(string pathName) { // C# のコードをアセンブリに変換 var assembly = CodeToAssembly(pathName); if (assembly != null) // アセンブリに変換されたプラグインを実行 Run(assembly); } // Python プラグインを実行 static void RunPython(string pathName) { dynamic plugin = Python.CreateRuntime().UseFile(pathName); // Python のコードからランタイムを作成し、 Run(plugin); // それを実行 } // プラグインを実行 static void Run(Assembly pluginAssembly) { // アセンブリを読み込み、その中から public な最初のクラスを取り出す var pluginType = pluginAssembly.GetExportedTypes().FirstOrDefault(type => type.IsClass); if (pluginType != null) // インスタンスを生成し、それをプラグインとして実行 Run(Activator.CreateInstance(pluginType)); } // プラグインを実行 static void Run(dynamic plugin) { Console.WriteLine(plugin.Name()); // プラグインの Name を表示し、 plugin.Run(); // プラグインを Run } // C# のコードをコンパイルしてアセンブリに変換 public static Assembly CodeToAssembly(string csharpCode) { using (var cscp = new CSharpCodeProvider()) { // コンパイルした結果のアセンブリを返す return cscp.CompileAssemblyFromFile(new CompilerParameters { GenerateInMemory = true }, csharpCode).CompiledAssembly; } } }
- DLL プラグインは前回と同じ。
アセンブリとして読み込んで、その中から public な最初のクラスを取り出し、中のメソッドを dynamic に実行する。 - C# プラグインはコンパイルし、メモリ上でアセンブリに変換する。後は DLL プラグインと同じ。
- Python プラグインは、コンパイルせずに、動的にスクリプトとして実行することでメソッドを呼び出す。
dynamic を使うことで、Python のプラグインも同じように実行することができる。
■ プラグイン処理の実行例
では、実行してみよう。
プログラムを実行しているフォルダーの下に Plugin と云う名前のフォルダーを作成し、そこに三つのプラグイン "Test.dll"、"Test.cs"、"Test.py" を置く。
本体プログラムの実行結果は次の通りだ。
DLL Plugin DLL Plugin is running! C# Plugin C# Plugin is running! Python plugin Python plugin is running!
各プラグインが実行された。
■ 今回のまとめ
今回は、前回のプラグイン処理に少し補足を行った。
Visual Basic.NET や F#、IronRuby 等も同様に扱えるのでないだろうか。