Contenu connexe Similaire à 他言語との連携(ネイティブから動的言語まで) (20) Plus de Tatsuya Ishikawa (20) 他言語との連携(ネイティブから動的言語まで)18. 初期化
ホストAPIで.Net呼び出し。
関数ポインタちょうだい。
ManagedExporter.dll
ManagedImporter
//関数ポインタをstaticに保持
ExportInfo s_Create(path, assembly, typeFullName);
void s_Free(handle);
・生成関数(delegate)
・解放関数(delegate)
・ホストAPI(メンド
い)
//COMインターフェイス取得
CLRCreateInstance(..., IID_PPV_ARGS(&pMetaHost));
pMetaHost->GetRuntime(..., IID_PPV_ARGS(&pRuntimeInfo));
pRuntimeInfo->GetInterface(..., IID_PPV_ARGS(&pClrRuntimeHost));
//ランタイム開始
pClrRuntimeHost->Start();
//文字列指定で.Netのstaticメソッドを呼び出し。
//型はint func(string args)
pClrRuntimeHost->ExecuteInDefaultAppDomain(...)
ObjectProxy::Init(L"v4.0.30319");
・delegateを関数ポインタに変換
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(func);
注意点としてdelegateのオブジェクトをGCで
回収されないようにする必要があります。
方法は複数あります。
・Static変数にする
・GC.KeepAlive
・GCHandle.Alloc
Win32App.exe
19. オブジェクト生成
ManagedExporter.dll
ManagedImporter
IInterfaceTable
Info s_Create(path, assembly, type);
・リフレクションで生成。
・ IInterfaceTableを使ってDelegateを取得。
・関数名でDelegateを取得できるDelegateを作成。
・ GCHandle.Allocで寿命を確保。
・ネイティブに返す。
InputDataDialog::InputDataDialog()
: ObjectProxy(AssemblyPathLocal,
"WpfGui.dll",
"WpfGui.InputDataDialog")
//マネージドクラス公開情報。
typedef void* (__stdcall *GetInterface)(name);
struct ExportInfo
{
HANDLE handle;
GetInterface getInterface;
};
WpfGui.dll
InputDataDialog : IInterfaceTable
Win32App.exe
InputDataDialog : ObjectProxy
21. 逆に.Netからネイティブのクラスを使う。
P/Invoke
void DisplayInfo::SetTitle(LPCWSTR title)
{
_strTitle = title;
}
HGLOBAL DisplayInfo::GetTitle()
{
size_t size = (_strTitle.length() + 1) * 2;
HGLOBAL global = ::GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, size);
memcpy((void*)global, _strTitle.c_str(), size);
return global;
}
//Exeでも公開関数を定義することは可能です。
extern "C"
{
__declspec(dllexport) void __cdecl DisplayInfoSetTitle(void* ptr, LPCWSTR title)
{
return ((DisplayInfo*)ptr)->SetTitle(title);
}
__declspec(dllexport) HGLOBAL __cdecl DisplayInfoGetTitle(void* ptr)
{
return ((DisplayInfo*)ptr)->GetTitle();
}
__declspec(dllexport) void __cdecl DisplayInfoDelete(void* ptr)
{
delete ptr;
}
}
22. class DisplayInfo : IDisposable
{
IntPtr _core;
public DisplayInfo(IntPtr core)
{
_core = core;
}
public string Title
{
get
{
IntPtr ptr = DisplayInfoGetTitle(_core);
string title = Marshal.PtrToStringUni(ptr);
Marshal.FreeHGlobal(ptr);
return title;
}
set
{
DisplayInfoSetTitle(_core, value);
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing && _core != IntPtr.Zero)
{
DisplayInfoDelete(_core);
_core = IntPtr.Zero;
}
}
23. [DllImport("Win32App.exe")]
static extern IntPtr DisplayInfoGetTitle(IntPtr core);
[DllImport("Win32App.exe", CharSet = CharSet.Unicode)]
static extern void DisplayInfoSetTitle(IntPtr core, string title);
[DllImport("Win32App.exe")]
static extern void DisplayInfoDelete(IntPtr core);
}
29. C#から使う。
internal static string Execute(InputData data)
{
//実行
var ir = IronRuby.Ruby.CreateRuntime();
ir.ExecuteFile(@"..formatter.rb");
//クラス生成
var type = ir.Globals.GetVariable("Formatter");
dynamic formatter = ir.Operations.CreateInstance(type);
//普通に使える
formatter.SetName(data.Name);
formatter.SetAge(data.Age);
formatter.SetLangauge(data.Langauge);
return (string)formatter.Format();
}
30. dynamicで表されるRubyのクラスの正体は?
class RubyObject : IRubyDynamicMetaObjectProvider
interface IRubyDynamicMetaObjectProvider : IDynamicMetaObjectProvider
public interface IDynamicMetaObjectProvider
{
DynamicMetaObject GetMetaObject(Expression parameter);
}
IDynamicMetaObjectProviderを実装したクラスをdynamicに入れると、
メソッド、プロパティー、フィールド解決時にGetMetaObjectを呼び出
してくれる。
なので、RubyObjectクラスがRubyに特化した解決をしてくれる。
*注)通常はIDynamicMetaObjectProviderではなく、
DynamicObjectを継承して実装します。
35. 以前は・・・
AppVar mainForm = _app[typeof(Application), "OpenForms"]()["[]"](0);
AppVar result = mainForm["MyMethod"]("100");
int value = (int)result.Core;
Assert.AreEqual(101, value);
dynamicを使うと・・・
dynamic mainForm = _app.Type<Application>().OpenForms[0];
int value = mainForm.MyMethod("100");
Assert.AreEqual(101, value);
やってることは、実質変わりませんが、可読性と学習効率がUpし
ました。
お客様に使ってもらっているのですが、サポートも減りました。