とまと あんらいぷ…

エンジニアの活動記録とかつぶやきとか

GitHub
スポンサードリンク

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

スポンサードリンク

C#からC++DLLを呼び出してマーシャリングの動きを確認した(中編)

C#からC++DLLを呼び出してマーシャリングの動きを確認した(前編)
の続きです。

前回はC#とC++のCOM相互運用(マーシャリング)について記載しました。
今回はタイトルの通り、C++とC#のプロジェクトを作成し、実際に呼び出してみます。

プロジェクト概要


以下にプロジェクトのイメージを絵にしています。
検証用にゲームの魔法使いをイメージしたクラスを作成しました。

サモナーがクリーチャー(モンスター)を呼び出すという
なんとも安直な発想ですが・・・

サモナークラスをC#で作成
クリーチャーをC++のDLLで作成しています。

イメージとしてはこんな感じ
20130308_solImage.png

サモナーがDLL呼び出しを使い、クリーチャーを召喚するという流れです。
では、さっそく実装部分をみていきましょう。

C++(アンマネージド)DLLの作成


クリーチャーを表現する関数を4体作ってみました。

レッドゴブリン



引数に文字列型を受け取り、引き渡された文字列に自分の名前をセットします。
文字セットはANSIです。C#側では文字セットはANSIだという宣言が必要です。

ブルーゴブリン



引数に文字列型を受け取り、呼び出された時、引き渡された文字列に自分の名前をセットします。
文字セットはUNICODEです。C#側では文字セットはUNICODEだという宣言が必要です。

ゴーレム



引数にクリーチャーのステータスを表現する構造体を持ちます。
ステータス構造体の内容は以下のとおり

C#側では、構造体メンバのすべての型とマッチングできる構造体を作成しDLLに渡します。

ドラゴン



ステータス構造体の配列を引数として持っています。
C#側ではDllImport 属性によって配列のサイズを指定します。

C++のプロジェクト全体ソースコードは以下のとおりです。

ヘッダファイル

クリーチャープロジェクトのソースファイル用ヘッダ情報を定義しています。

ソースファイル

クリーチャープロジェクトの実装です。
私C++は全く詳しくないので、記載方法にご指摘があったら教えて下さい。
特に配列の初期化部分・・・よい案をください。

エクスポート用ファイル

出力するために必要な宣言部分

C#(マネージド)コードからの呼び出し


先にごめんなさいしておきますが、このコードはVisual StudioのDebug構成で実行すると「メモリが壊れる」とかいう
メッセージが出てきて落ちます。
char**とstringの相互変換は未サポートのようです。(C#もcharが正しい)
無理矢理 string でrefなんてするから悪かったんです・・・すみません。

まぁ、Release構成で実行すると動くからいいのです!!!メモリ壊しながら動けー!
(正しい実装をする場合、stringじゃなくてcharでやってね・・・)

ではでは、C#の呼び出し方法を記載します。
C++とC#は以下のサイトを参考に調べていきます。

CLR 徹底解剖 マネージ コードとアンマネージ コード間でマーシャリングする

レッドゴブリンの召喚



"Creatures.dll"の"RedGoblin"関数を呼び出す宣言をしています。 CharSet=CharSet.Ansiの部分で、相手がANSIだと指定
ref string 指定してるので[In,Out]を前につけています。[In,Out]は既定の動作なので不要ですが、きっちり記載しました。
あと戻り値ですが、C++側のLong型は、C#ではintなので合わせています。
また、static externとして指定する必要があります。

ブルーゴブリンの召喚



DLLの呼び出しと呼び出し関数名は省略します。
[MarshalAs( UnmanagedType.LPWStr )]の部分ですが、マーシャラに対して、アンマネージドの型は何型だ?
を指定することができます。これを間違えると怪しい値が帰ってくるので注意です。
今回は相手型(C++)はwchar_t *です。LPWStrはUNICODEの事なので、これで問題ないです。
で、DLL側で値の変更をしてほしいのでStringBuilderクラスを渡します。お決まりです。

ゴーレムの召喚



ステータス構造体を引数にとってます。
C++の構造体部分はC#で合うように宣言して同じ呼び出しを実現しました。

byte[] Weakness; ですが、[MarshalAs( UnmanagedType.ByValArray, SizeConst=7 )]
とうふうに、構造体のメンバに対して属性指定ができます。
C++側ではunsigned char=BYTEとして宣言してます。
C#側ではそのまま素直にbyte宣言して、サイズ7を指定します。

ドラゴンの召喚



一番上のStatus構造体はゴーレムの召喚部分で記載しているものと同じです。
その配列が7つなのでCerberusHeads構造体を作成
その内部で[MarshalAs( UnmanagedType.ByValArray, SizeConst=128 )]として指定しています。
サイズの計算が大変・・・

さて、C++の呼び出し部分ができたので
この呼出部分を実行するプログラムを含めた全体コードはこちら。

MagicCast.cs
DllImportの実装をまとめたクラスです。詠唱系です。
上記記載以外のドラゴンの召喚部分も記載されてますが、とりあえず放置でお願いします。


Program.cs
MagicCastクラスを呼び出して、返却された値などの確認をするクラスです。

冗長的にコーディングしてる部分にツッコミされると多分凹みます。
とりあえずマーシャリング部分を見たかったんだもん。

実行しよう!


さてさてvoid Mainから動く挙動を見て行きましょう。

20130311_run.png
・・・・
いけてそうですね。
ちゃんとC++側でセットした内容がC#側で取得できてますね。


とまぁ、普通に呼び出すだけならこれで終わりなのですが・・・
実はマーシャリングの動きっていうのは意外とオーバーヘッドがかかるんです。
マネージドとアンマネージド部分をいちいち翻訳してるわけですから、
繰り返し実行すると当然、レスポンスに影響が出てきます。

後編で記載しますが
例えばドラゴンを10,000回呼び出すと、3.6秒ほどかかります。
1万回で3.6秒もかかってるので、そこそこなコストですね。

これを「メモリの固定化」という技術?を使えば
C#側で使っているメモリ部分を翻訳無しでC++側に渡すことができます。

20130311_run100000.png

結果、1万回で0.05秒で呼び出し終わり!早い!

後編では、DLL呼び出し時におけるメモリの固定化という観点で
DLL呼び出しを高速化す方法を見て行きましょう。

次回に続く!
▼この記事を読んだ方は、こんな記事も読んでいます。▼

スポンサードリンク

テーマ:プログラミング - ジャンル:コンピュータ

コメント

意外と・・・

解説乙です。やっぱり、インターフェイスは再定義しないといけないんですね・・・まあ、
当たり前かw ってか、C#とC++を混在させることって出来たら嬉しいかも。最悪、C++のほうも、マネージドコードになってもいいから、そういう感じで使えないものですかね・・・やっぱり、既存のソースを変更なしでぽんと使えるほうが嬉しい気がします。ってか、どうせならC#にプリプロセッサを追加して、C#とC++を混在させ、C++のほうだけアンマネージドに出来たら最強かも。上のサンプルを見る限り、C++のクラス定義のみ(*.hに実装を書かない)って制約条件のもとなら、トランスレーターが結構簡単に書けそうな気もします(もちろん、僕じゃなくてデキる人たちなら、ってことですが)。C++のヘッダからC#用のインターフェイスを自動生成してビルド・・・そういう仕組みってないものか。

名無しの通りすがりさん
読んでくれたのですね(笑)ありがとうございます。今読み返すとひどい解説です。
さてさて、C#とC++の混在。つまりはアンマネージとマネージの混在については、実はそれ専用の言語が存在しちゃったりしてます。
そやつは「C++/CLI」というものでwikiによると「C++/CLIは、.NET Frameworkの共通言語基盤 (CLI)上で実行するプログラムを作るためにC++を拡張したプログラミング言語である。」ということです。まさにうってつけ!
ちょっと構文がややこしいのですが、C#でC++を使いたくてごにょごにょするよりはよっぽど建設的です。
で、C#は所詮マネージコードに特化した設計なので、C++/CLIを覚えて、C#->CLI->C++という呼び出しをすることで可読性が上がると思ってます(多分)
当エントリを作成した理由は、いかんともなき理由により、ブラックボックス化されたC++をC#で呼び出す必要があり、なおかつ速度がかなり求められたので今後のために保存しておきました。
本当はこんなことしたくなかった・・・


  • 2014/07/23(水) 00:56:41 |
  • URL |
  • 管理人 #z9jllgzo
  • [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://dalmore.blog7.fc2.com/tb.php/62-a31c0db1
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。