他言語との連携(ネイティブから動的言語まで)

36
他言語との連携 ネイティブから動的な言語まで。 (別言語からC#Wellcom) C#の素晴らしさを語る会

Upload: tatsuya-ishikawa

Post on 22-Jul-2015

1.689 views

Category:

Technology


8 download

TRANSCRIPT

Page 1: 他言語との連携(ネイティブから動的言語まで)

他言語との連携ネイティブから動的な言語まで。

(別言語からC#にWellcom)

C#の素晴らしさを語る会

Page 2: 他言語との連携(ネイティブから動的言語まで)

自己紹介

名前石川達也

所属株式会社Codeer代表取締役(http://www.codeer.co.jp/)

技術C、C++、C++/CLI、C#、少しJava組み込みとか、Windowsアプリとか。

趣味ギター

Page 3: 他言語との連携(ネイティブから動的言語まで)

まだまだ、ネイティブアプリは世の中にあふれている!

まずは、ネイティブとの連携

Page 4: 他言語との連携(ネイティブから動的言語まで)

Win32アプリのメンテをしています。

そして、ある日上司から唐突に作業依頼がやってきました。

ストーリー

Page 5: 他言語との連携(ネイティブから動的言語まで)

次の仕様追加で作る画面、WPFで作ってくれない?流行ってんでしょ?

流行ってるか・・・?

Page 6: 他言語との連携(ネイティブから動的言語まで)

(でたよ、思い付き・・・)

でも、あなたは、答えました。

「Yes, sir」なぜなら、WPFで作るということは・・・。

Page 7: 他言語との連携(ネイティブから動的言語まで)

大好きなC#を仕事で使えるから!

Page 8: 他言語との連携(ネイティブから動的言語まで)

備考

でも、この思い付きは意外と良いんです。

Nativeアプリを一気に.Netで作り変えるのは大変!

その過程で、チームに.Netのノウハウが溜まるのです。

Page 9: 他言語との連携(ネイティブから動的言語まで)

方法は複数あり、それぞれ長所短所がある。

・ネイティブをC++/CLIにする

・C++/CLIでブリッジをつくる

・COM相互運用

・P/Invoke

・ホストAPI

私はこれをよく使ってます

Page 10: 他言語との連携(ネイティブから動的言語まで)

今回はC#とC++のみで、やってみます。

・ネイティブをC++/CLIにする

・C++/CLIでブリッジをつくる

・COM相互運用

・P/Invoke

・ホストAPI

割愛

Page 11: 他言語との連携(ネイティブから動的言語まで)

レシピはこんなもんかな・・・

Page 12: 他言語との連携(ネイティブから動的言語まで)

8Hほど煮込みました。

Page 13: 他言語との連携(ネイティブから動的言語まで)

あれ?

Page 14: 他言語との連携(ネイティブから動的言語まで)

これ、いつもやってるやつより良くね? (゚д゚ノ)ノ

なんで、今までC++/CLI使ってたの? (T-T)

ていうか、やっぱり・・・・。

Page 15: 他言語との連携(ネイティブから動的言語まで)

C#って素晴らしい!

では、コードをどうぞ~。

弊社サイトからDwonLoad可能です。

Codeerで検索→技術メモ→ネイティブと.Netの連携http://www.codeer.co.jp/technical-notes/NativeAndNet

Page 16: 他言語との連携(ネイティブから動的言語まで)

ManagedExporter.dll

IInterfaceTable

ManagedImporter

ObjectProxy

WpfGui.dll Win32App.exe

InputData : IInterfaceTable

InputDataDialog : IInterfaceTable

InputData : ObjectProxy

InputDataDialog : ObjectProxy

★最初にObjectProxy::Initを一回呼びます。

概略図

Page 17: 他言語との連携(ネイティブから動的言語まで)

まとめると、

Delegateを関数ポインタにしてネイティブから使おう!

って作戦です。

Page 18: 他言語との連携(ネイティブから動的言語まで)

ManagedExporter.dll

ManagedImporter

//関数ポインタをstaticに保持ExportInfo s_Create(path, assembly, typeFullName); void s_Free(handle);

Win32App.exe

ObjectProxy::Init(L"v4.0.30319");

ホストAPIで.Net呼び出し。関数ポインタちょうだい。

・生成関数(delegate)・解放関数(delegate)

//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(...)

IntPtr ptr = Marshal.GetFunctionPointerForDelegate(func);

注意点としてdelegateのオブジェクトをGCで回収されないようにする必要があります。方法は複数あります。

・Static変数にする・GC.KeepAlive

・GCHandle.Alloc

・ホストAPI(メンドい)

・delegateを関数ポインタに変換

初期化

Page 19: 他言語との連携(ネイティブから動的言語まで)

ManagedExporter.dll

IInterfaceTable

ManagedImporter

Info s_Create(path, assembly, type);

WpfGui.dll Win32App.exe

InputDataDialog : IInterfaceTable InputDataDialog : ObjectProxy

InputDataDialog::InputDataDialog()

: ObjectProxy(AssemblyPathLocal,

"WpfGui.dll",

"WpfGui.InputDataDialog")

・リフレクションで生成。・ IInterfaceTableを使ってDelegateを取得。・関数名でDelegateを取得できるDelegateを作成。・ GCHandle.Allocで寿命を確保。・ネイティブに返す。

//マネージドクラス公開情報。typedef void* (__stdcall *GetInterface)(name);

struct ExportInfo

{

HANDLE handle;

GetInterface getInterface;};

オブジェクト生成

Page 20: 他言語との連携(ネイティブから動的言語まで)

ManagedExporter.dll

IInterfaceTable

ManagedImporter

Info s_Create(path, assembly, type);

Win32App.exe

InputDataDialog : ObjectProxy

BIND_PROXY_METHOD(SetOwner);BIND_PROXY_METHOD(DoModal);BIND_PROXY_METHOD(GetInputData);

//マネージドクラス公開情報。typedef void* (__stdcall *GetInterface)(name);

struct ExportInfo

{

HANDLE handle;

GetInterface getInterface; ←これを使う};

インターフェイスマッピング

Page 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;

}

}

Page 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;

}

}

Page 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);}

Page 24: 他言語との連携(ネイティブから動的言語まで)

動的言語との連携

dynamic

Page 25: 他言語との連携(ネイティブから動的言語まで)

次の指令が来ました。

Page 26: 他言語との連携(ネイティブから動的言語まで)

その、画面の処理、ユーザにスクリプトでカスタマイズさせたりできない?

Rubyとか。

流行ってるんでしょ?

Windowsではそうでもないと思うけど・・・

Page 27: 他言語との連携(ネイティブから動的言語まで)

ま、Rubyには興味あったし、やってみるか!

Page 28: 他言語との連携(ネイティブから動的言語まで)

class Formatter

def SetName(name)

@name = name

end

def SetAge(age)

@age = age

end

def SetLangauge(langauge)

@langauge = langauge

end

def Format

@name + "\r\n" + @age.to_s + "\r\n" + @langauge;

end

end

Rubyのコード

Page 29: 他言語との連携(ネイティブから動的言語まで)

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();}

C#から使う。

Page 30: 他言語との連携(ネイティブから動的言語まで)

class RubyObject : IRubyDynamicMetaObjectProvider

dynamicで表されるRubyのクラスの正体は?

interface IRubyDynamicMetaObjectProvider : IDynamicMetaObjectProvider

IDynamicMetaObjectProviderを実装したクラスをdynamicに入れると、メソッド、プロパティー、フィールド解決時にGetMetaObjectを呼び出してくれる。なので、RubyObjectクラスがRubyに特化した解決をしてくれる。

*注)通常はIDynamicMetaObjectProviderではなく、DynamicObjectを継承して実装します。

public interface IDynamicMetaObjectProvider

{

DynamicMetaObject GetMetaObject(Expression parameter);

}

Page 31: 他言語との連携(ネイティブから動的言語まで)

使う側は優雅!

RubyObjectが頑張ってくれている。

Page 32: 他言語との連携(ネイティブから動的言語まで)

Dynamicは、これだけでなく、

動的なインターフェイスを持つライブラリに優雅なインターフェイスを与えてくれる!

XML操作とか

COMとかね。

あなたも、そんなライブラリ持っていませんか?

Page 33: 他言語との連携(ネイティブから動的言語まで)

Codeeer.Friendly.Dynamic

弊社にはあります。

http://www.codeer.co.jp/AutoTest

無料

Page 34: 他言語との連携(ネイティブから動的言語まで)

対象プロセスを

強力な操作方法(おそらく世界で唯一)でテスト用に操作するライブラリです。

軽くデモ・・・

Page 35: 他言語との連携(ネイティブから動的言語まで)

以前は・・・

やってることは、実質変わりませんが、可読性と学習効率がUpしました。お客様に使ってもらっているのですが、サポートも減りました。

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);

Page 36: 他言語との連携(ネイティブから動的言語まで)

ご清聴ありがとうございました。