連載:深入り.NETプログラミング

アンマネージ・コードの型情報と連携する

NyaRuRu
Microsoft MVP Windows - DirectX(Jan 2004 - Dec 2009)
2009/07/21
Page1 Page2

 C#やVisual Basicなどのコードからアンマネージ・コード(ネイティブ・コード)を呼び出すための「P/Invoke(Platform Invoke、プラットフォーム呼び出し)」には定型作業が多い。例えばC/C++用のマクロ定数は、C#ではenum型として扱いたいところだ。これには例えば次のような書き換えが必要になる。

#define PAGE_NOACCESS          0x01
#define PAGE_READONLY          0x02
#define PAGE_READWRITE         0x04
#define PAGE_WRITECOPY         0x08
(以下略)
C/C++用のマクロ定数定義の例

[Flags]
public enum MemoryProtection : uint
{
  PAGE_NOACCESS = 0x01,
  PAGE_READONLY = 0x02,
  PAGE_READWRITE = 0x04,
  PAGE_WRITECOPY = 0x08,
(以下略)
}
C/C++用のマクロ定数をC#のenum型で表現した例

 これを実現する安直な方法は、この作業をすべて手で行うというものだ。まずはオリジナルのC/C++コードをC#コード上にコピーし、微妙な文法の違いを行ごとに書き換えていく。義務感に駆られて<summary>タグによるドキュメント・コメントも付け始めると、作業量はさらに増える。Document Explorerで該当するドキュメントを探し、ドキュメント・コメントに1つ1つコピーすれば、とても親切なP/Invokeコードが出来上がることだろう。

 このような手作業方式は、ひたすらに手を動かすだけあって、なんだかずいぶん仕事をした気になってしまう。筆者もよく現実逃避にP/Invokeコードを書いてしまうのだが、自戒も込めて、こんな逃避先はつぶしてしまいたい。というわけで、今回はC/C++と.NETの橋渡しに関する使えそうな技術を紹介しよう。

【コラム】エディタの機能で楽をする

 たいていのエディタには、記録したキー操作を繰り返すマクロ機能や、強力なテキスト置換の機能が付いている。うまく一発で変換できるマクロを作れたときの達成感はひとしおだ。これまた仕事をした気になってしまう。


【コラム】pinvoke.net

 P/Invokeといえば、http://pinvoke.netに投稿されているコードを使うという方法もある。筆者はあまりこのサイトを使っていないのだが、深い理由があるわけではなく、ドキュメント・コメントの付け方がまちまちであったり、SafeHandle型を使うべきところでIntPtr型を使っているものが混ざっていたりと、コードの質がバラバラなのが単に気になるというだけである。なお、一部のAPIには非常に詳細な解説が付いており、それが忘れていたことや重要な注意点を思い出させてくれることもある。

P/Invoke Interop Assistant

 P/Invokeには、実に便利なツールがすでにある。Code Plex上のCLR Interopチームのサイトで公開されている「P/Invoke Interop Assistant」というツールは、P/Invokeまたは、逆P/Invokeで用いる型定義の自動生成を行ってくれる。

図1 P/Invoke Interop Assistantの実行例:[SigImp Search]タブ
[SigImp Search]タブからは、windows.hファイルに含まれる型情報を検索することができる。P/Invokeコードを生成したい要素を選択し、[Generate]ボタンを押すと、右側のペインにソース・コードが生成される。この画面の例ではC#となっているが、Visual Basicでコードを生成することも可能だ。事前作成された型情報データベースを使用するので、実行環境にWindows SDKは必要ない。

図2 P/Invoke Interop Assistantの実行例:[SigImp Translate Snippet]タブ
[SigImp Translate Snippet]タブからは、C++のソース・コードを基に、その場でP/Invokeコードを生成することもできる。例えばビット・フィールドを用いた構造体もこのとおり。

 さて、P/Invoke Interop Assistantはどのようにコードを生成しているのだろうか? ソース・コードが公開されているので、その実装方法を調べてみた。

 結論からいえば、P/Invoke Interop AssistantはC/C++用の「手書きパーサー」を内蔵している。ただし、C/C++文法を完全に網羅しているわけではなく、あくまでWindows SDKのヘッダ・ファイルに登場する程度の文法をサポートするぐらいのものと考えればよいだろう。それでも大仕事である。それを既存のライブラリを一切使わず、自前のパーサーでやってのけるのには恐れ入る。テストもしっかり書かれており、PInvokeTool\UnmanagedToManaged\PInvokeTest\SampleFiles以下に定義されているテスト・ケースは58にも及ぶ。ちなみにこのパーサー、実装言語はVisual Basicである。

 また、プリプロセッサ・マクロの解釈も部分的にサポートしている。先ほど「#define」で定義された定数の取り込みに成功していたことを思い出してほしい。Windows SDKのヘッダ・ファイルというある種のDSL(ドメイン特定言語)のインポートに特化したパーサーといえる。


 INDEX
  [連載]深入り.NETプログラミング
  アンマネージ・コードの型情報と連携する
  1.C/C++と.NETの橋渡し/P/Invoke Interop Assistant
    2.Standard Annotation Language(SAL)/デバッグ・シンボル

インデックス・ページヘ  「深入り.NETプログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間