初めに
「IronPythonを遅延バインディングで呼び出してみた」の続き
IronPyhton2.7系列から、今度はIonPython3.4系列にも挑戦してみた。
静的バインディング
まずは、静的バインディング
普通に、VisualStudioで「参照」をすれば、IronPython2.7.x系のコードで、.NETアプリ(c#)から、Pyhtonコードを呼び出すことができた。
何もコードを書き換えなくていいのは、良い♪。
参照するDLLは以下
- [IronPython3.4.0インストールディレクトリ]\IronPython.dll
- [IronPython3.4.0インストールディレクトリ]\Microsoft.Scripting.dll
遅延バインディングへの道1(GetTypesメソッドの罠)
いよいよ、遅延バインディング
サンプルコードは「IronPythonを遅延バインディングで呼び出してみた」にあるものをそのまま使って、改造していく。
これもそのままOKって感じでイケるんじゃん!?...と思いきや
IronPython.dllのAssenblyクラスのGetTypes()メソッドが、Typeの配列を返さないらしい。....orz
ふと、for文の上と中のGetType()メソッドの引数が、"Hosting.Python"と"IronPython.Hosting.Python"で異なることを発見。
Type type = lib.GetType("Hosting.Python");
if (type == null) {
type = lib.GetType("IronPython.Hosting.Python");
}
if (type == null) {
Type[] typeHako = null;
typeHako = lib.GetTypes(); // GetTypes() はIronPython3.4.0の場合はエラーとなる
for (int i = 0; i < typeHako.Length; i++) {
if (typeHako[i].FullName == "IronPython.Hosting.Python") {
type = typeHako[i];
break;
}
}
}
こうすることで、問題解決。
遅延バインディングへの道2(ModuleResolveイベントで撃沈)
次は、
というエラー。
IronPython.dllと同じディレクトリ下のMicrosoft.Scripting.dllが見つからない(ロードしてくれない)ようだ。...orz
IronPython.dllと同じディレクトリなんだからMicrosoft.Scripting.dllも一緒にロードしてくれよ。と思いつつ...orz
DLLの検索パスをいじれらなきゃダメ!?...いやいやアプリケーションプログラマに検索パスいじるとかハードル高すぎでしょ、、、、などと、いろいろと調べてみると、System.Reflection.AssemblyにはModuleResolveイベントというモジュールのロードに失敗したら呼ばれるイベントがあるらしい。....これが使えないかと、こんなコードに修正。
ちなみに、APIリファレンスはここ
つまり、↓のような関数を定義しておいて・・・
private static Module evModuleResolve(object sender, ResolveEventArgs e)
{
Assembly lib = Assembly.LoadFile("C:\\Program Files\\IronPython 3.4\\Microsoft.Scripting.dll"); // ← ここにブレイクポイントを設置してそもそも呼ばれるかどうか確認する
Module m = lib.Modules.First();
return m;
}
ここにブレイクポイントを設置して・・・
lib.ModuleResolve += new ModuleResolveEventHandler(evModuleResolve);
な感じで、ModuleResolveイベントハンドラに割り当てて・・・
MethodInfo createEngine = type.GetMethod("CreateEngine", new Type[] { }, null);
↑のところのエラーで、先ほどのイベントハンドラを呼んでくれるかどうか・・・
・・・むむむ・・・
結論としては、イベントハンドラが呼ばれることはなかった(ブレイクポイントで止まらなかった)...orz
まぁ、ロード対象は、モジュール(Module)じゃ違うような、、、アセンブリ(Assenbly)のロード失敗には何かないかな!?・・・
遅延バインディングへの道3(AssemblyResolveイベントの希望)
さらに探していると、System.AppDomainとかいうクラスがあり、それにAssemblyResolveイベントハンドラというものがあるらしい。
ちなみに、APIリファレンスはここ
ということで、↓のような関数を定義しておいて・・・
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
Console.WriteLine("Resolving..."); // ← ここにブレイクポイントを設置してそもそも呼ばれるかどうか確認する
return null;
}
ここにブレイクポイントを設置して・・・
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
な感じで、AssemblyResolveイベントハンドラに割り当てて・・・
さて・・・
MethodInfo createEngine = type.GetMethod("CreateEngine", new Type[] { }, null);
↑のところのエラーで、先ほどのイベントハンドラを呼んでくれるかどうか・・・
・・・結論としては、イベントハンドラ、呼ばれた!!
こんな感じでイベントハンドラが呼ばれている♪
遅延バインディングへの道4(AssemblyResolveイベントを整理整頓)
ということで、定義した関数を↓のように修正
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
Assembly liblib = null;
String IronPythonDir = "C:\\Program Files\\IronPython 3.4\\";
// IronPython以外のロードエラーには関わりたくないので、スルーしたい
if (args.RequestingAssembly.Location.StartsWith(IronPythonDir) == true) {
// args.Nameは
// 「Microsoft.Scripting, Version=1.3.3.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1」
// みたいな感じだから、カンマの前までを抽出する
String[] hako = args.Name.Split(new char[] { ',' });
String str = IronPythonDir + "\\" + hako[0] + ".dll";
// IronPythonのインストールディレクトリ以下にDLLが存在する時だけロードする
if (File.Exists(str) == true) {
liblib = Assembly.LoadFile(str);
}
}
return liblib;
}
・・・結論としては、Pythonスクリプトが実行された・・・ので、ヨシ!!
最後に、ハードコーディングされている「C:\Program Files\IronPython 3.4\」については、インストール先ディレクトリを保持しているレジストリから取ってくるようにして完成・・・というわけです。
遅延バインディングへの道5(AppDomain)
そもそも、しがないアプリケーションプログラマがAppDomainをいじっていいのか!?
とか、
(「これ」と「これ」)は、スクリプト機能は、プラグインとして本体EXEとは分離して別DLLにしているんだけど、、、
そういう状態でも、AppDomainいじっていいのかな!?
まぁ、(「これ」と「これ」)は、私一人で開発しているので、問題になるようなことはないかもしれないけど・・・!?
もしかして、
(「これ」と「これ」)はプラグイン機能として、IPlugin.dllという独立したDLLなので、この中でAppDomainを新規に作り出した方がいいのかな!?
AppDomainについて記事を見つけた→「連載:明解.NETテクノロジ アプリケーション・ドメイン Application Domain」
まとめ
なんとか、IronPython3.4.x系でも、バージョンごとにコンパイルしたバイナリを事前用意しなくてよくなった。
(2023年6月!?ぐらいに、ver3.4.1が出ていたので、いつの間にかにver3.4.x系もマルチバージョンになっていた)
さらに、IronPython2.7.x系とIronPython3.4.x系を同一バイナリに出来たのはうれしい。