他言語との連携
ネイティブから動的な言語まで。
(別言語からC#にWellcom)

C#の素晴らしさを語る会
自己紹介
名前
石川達也
所属
株式会社Codeer代表取締役
(http://www.codeer.co.jp/)
技術
C、C++、C++/CLI、C#、少しJava
組み込みとか、Windowsアプリとか。
趣味
ギター
まずは、ネイティブとの連携
まだまだ、ネイティブアプリは世の中にあふれて
いる!
ストーリー
Win32アプリのメンテをしています。

そして、ある日上司から唐突に作業依頼がやってきました。
次の仕様追加で作る画面、
WPFで作ってくれない?
流行ってんでしょ?

流行ってる
か・・・?
(でたよ、思い付き・・・)
でも、あなたは、答えました。

「Yes, sir」
なぜなら、WPFで作るということは・・・。
大好きなC#を仕事で使えるか
ら!
備考

でも、この思い付きは意外と良いんです。
Nativeアプリを一気に.Netで作り変えるのは大変!
その過程で、チームに.Netのノウハウが溜まるのです。
方法は複数あり、それぞれ長所短所があ
る。
・ネイティブをC++/CLIにする
・C++/CLIでブリッジをつくる
・COM相互運用
・P/Invoke

・ホストAPI

私はこれをよく使って
ます
今回はC#とC++のみで、やってみ
ます。 ・ネイティブをC++/CLIにする

割愛

・C++/CLIでブリッジをつくる
・COM相互運用
・P/Invoke

・ホストAPI
レシピはこんなもんかな・・・
8Hほど煮込みまし
た。
あれ?
これ、いつもやってるやつより良くね? (゚
д゚ノ)ノ
なんで、今までC++/CLI使ってたの? (T-T)
ていうか、やっぱり・・・・。
C#って素晴らし
い!
では、コードをどうぞ~。

弊社サイトからDwonLoad可能です。
Codeerで検索→技術メモ→ネイティブと.Netの連携
http://www.codeer.co.jp/technical-notes/NativeAndNet
概略図

ManagedExporter.dll

ManagedImporter

IInterfaceTable

ObjectProxy

WpfGui.dll

Win32App.exe

InputDataDialog : IInterfaceTable

InputDataDialog : ObjectProxy

InputData : IInterfaceTable

InputData : ObjectProxy

★最初にObjectProxy::Initを一回呼びます。
まとめると、

Delegateを関数ポインタにしてネイティブから使
おう!
って作戦です。
初期化
ホスト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
オブジェクト生成

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
インターフェイスマッピング

ManagedExporter.dll
IInterfaceTable

ManagedImporter
Info s_Create(path, assembly, type);

BIND_PROXY_METHOD(SetOwner);
BIND_PROXY_METHOD(DoModal);
BIND_PROXY_METHOD(GetInputData);
//マネージドクラス公開情報。
typedef void* (__stdcall *GetInterface)(name);
struct ExportInfo
{
HANDLE handle;
GetInterface getInterface; ←これを使う
};

Win32App.exe

InputDataDialog : ObjectProxy
逆に.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;
}
}
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;
}
}
[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);
}
dynamic
動的言語との連携
次の指令が来ました。
その、画面の処理、ユーザにスクリプト
で
カスタマイズさせたりできない?

Rubyとか。

流行ってるんでしょ?

Windowsではそうでもな
いと思うけど・・・
ま、Rubyには興味あったし、
やってみるか!
Rubyのコード
class Formatter
def SetName(name)
@name = name
end
def SetAge(age)
@age = age
end

def SetLangauge(langauge)
@langauge = langauge
end
def Format
@name + "rn" + @age.to_s + "rn" + @langauge;
end
end
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();
}
dynamicで表されるRubyのクラスの正体は?
class RubyObject : IRubyDynamicMetaObjectProvider

interface IRubyDynamicMetaObjectProvider : IDynamicMetaObjectProvider

public interface IDynamicMetaObjectProvider
{
DynamicMetaObject GetMetaObject(Expression parameter);
}
IDynamicMetaObjectProviderを実装したクラスをdynamicに入れると、
メソッド、プロパティー、フィールド解決時にGetMetaObjectを呼び出
してくれる。
なので、RubyObjectクラスがRubyに特化した解決をしてくれる。
*注)通常はIDynamicMetaObjectProviderではなく、
DynamicObjectを継承して実装します。
使う側は優雅!

RubyObjectが頑張ってくれている。
Dynamicは、これだけでなく、
動的なインターフェイスを持つライブラリに優雅なインターフェイスを与え
てくれる!
XML操作とか

COMとかね。
あなたも、そんなライブラリ持っていませんか?
弊社にはあります。
無料

Codeeer.Friendly.Dynamic

http://www.codeer.co.jp/AutoTest
対象プロセスを
強力な操作方法(おそらく世界で唯
一)で
テスト用に操作するライブラリです。

軽くデ
モ・・・
以前は・・・
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し
ました。
お客様に使ってもらっているのですが、サポートも減りました。
ご清聴ありがとうございました。

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