++C++; // 未確認飛行 C

プラットフォーム呼び出し

目次

キーワード

概要

(書きかけ)

.NET Framework は、100% マネージドコードだけで何でもやろうとは思っていなくて、 プラットフォーム依存のネイティブコード呼び出し機能も持っています。 このような機能を、P/Invoke(Platform Invoke: プラットフォーム呼び出し)と呼びます。

.NET Framework が標準で持つライブラリはかなり高機能でいろいろなものがそろっていますが、 OS に深く食い込むような部分はどうしても P/Invoke が必要になってきます。

また、過去に C/C++ で書かれたような資産を生かすためにも P/Invoke が利用されます。 (この用途の場合、P/Invoke 以外に、C++/CLI を使うという選択肢もあります。)

ポイント
  • .NET Framework はネイティブコード呼び出しようの命令を持っている。
  • C# でネイティブコード呼び出しをするには、DllImport 属性とかを使う。

予定

C++/CLI       (sp_unsafe.htmlから移す)
DllImport
ComImport


用語
thunk, stab: 関数呼び出し用のつなぎ役
RCW (Runtime Callable Wrapper): .NET から COM を呼ぶためのラッパー。thunk の COM 版。
CCW (COM Callable Wrapper): RCW の逆。.NET クラスを COM として公開。

Marshaling: managed な型 → native な型への変換
thunk とか RCW が内部でやってくれてる。
(.NET Framework に組み込まれてる機能)


extern キーワード(キーワードページにも追加)
    

COM

  • (普通は手書きするものじゃないけども、)ComImport 属性とかつけてクラスを定義
  • タイプ ライブラリ インポータ (Tlbimp.exe)使って自動生成するのが普通
  • Visual Studio 使ってるなら、「参照の追加」→「COM」で自動生成

COM の参照

  • Early Bind
  • Tlbimp もしくは「参照の追加」
  • Tlbimp とかに頼るならかなり簡単。 ただ、 呼びもしない分まで含めて、全部のメソッドの Runtime Callable Wrapper 作っちゃうみたいで、 ばかでかい COM DLL を使うと、RCW のサイズが悲惨な事に。
  • PIA(Primary Interop Assembly: COM の発行元が提供するオフィシャルな RCW)依存。 バージョン管理地獄。

以下のコード、コンパイルするためには 「参照の追加」→「COM」→「Microsoft XML, v6.0」

var doc = new MSXML2.DOMDocument60Class();
doc.load("sample.xml");

var elem = doc.documentElement;
var items = elem.getElementsByTagName("Item");

foreach (var item in items.Cast<MSXML2.IXMLDOMElement>())
{
    var name = item.getAttribute("Name") as string;
    var val = item.getAttribute("Value") as string;

    Console.WriteLine("{0} = {1}", name, val);
}
<?xml version="1.0" encoding="utf-8" ?>
<Sample>
  <Item Name="a" Value="1"/>
  <Item Name="b" Value="2"/>
  <Item Name="c" Value="3"/>
  <Item Name="d" Value="4"/>
</Sample>
a = 1
b = 2
c = 3
d = 4

参考: C++ での COM 利用

  • C++ でも事前に型情報が必要。
  • #import (VC++ の機能)で COM DLL から型情報取れる。
#include "stdafx.h"

#import <msxml6.dll>

typedef MSXML2::IXMLDOMDocument3Ptr IDocument;
typedef MSXML2::IXMLDOMNodeListPtr  INodeListPtr;
typedef MSXML2::IXMLDOMNodePtr      INodePtr;
typedef MSXML2::IXMLDOMElementPtr   IElementPtr;

int _tmain(int argc, _TCHAR* argv[])
{
  // COMライブラリの初期化

  CoInitialize(NULL);

  {
    // ドキュメントのインスタンスを作成

    IDocument pDoc;
    pDoc.CreateInstance( __uuidof(MSXML2::DOMDocument60) );
    pDoc->async = VARIANT_FALSE; 

    //  XMLファイルのロード

    pDoc->load("sample.xml");

    // Item ノードを取得

    IElementPtr pElem = pDoc->GetdocumentElement();
    INodeListPtr plItem = pElem->getElementsByTagName("Item");

    // Item ノードの Name と Value を表示

    for(int i=0; i<plItem->Getlength(); i++)
    {
      IElementPtr  peItem = plItem->Getitem(i);
      
      BSTR name = peItem->getAttribute("Name").bstrVal;
      BSTR val  = peItem->getAttribute("Value").bstrVal;

      wprintf(L"%s = %s\n", name, val);
    }
  }

  // COMライブラリの終了処理

  CoUninitialize();

  return 0;
}

遅延バインド

  • Late Bind
  • Type.GetTypeFromProgID とか Type.GetTypeFromCLSID とか
  • Activator.CreateInstance
  • 正直、C# 3.0 以前じゃやってらんない。
  • CreateInstance した時点で PIA が作られるっぽい。
// DOMDocument60Class
Type t = Type.GetTypeFromCLSID(
    new Guid("88D96A05-F192-11D4-A65F-0040963251E5"));

var doc = Activator.CreateInstance(t);
t.InvokeMember("load", BindingFlags.InvokeMethod, null, doc, new object[] { "sample.xml" } );

var elem = t.InvokeMember("documentElement", BindingFlags.GetProperty, null, doc, new object[0]);
var items = elem.GetType().InvokeMember("getElementsByTagName", BindingFlags.InvokeMethod, null, elem, new object[] { "Item" }) as IEnumerable;

foreach (var item in items)
{
    var name = item.GetType().InvokeMember("getAttribute", BindingFlags.InvokeMethod, null, item, new object[] { "Name" }) as string;
    var val = item.GetType().InvokeMember("getAttribute", BindingFlags.InvokeMethod, null, item, new object[] { "Value" }) as string;

    Console.WriteLine("{0} = {1}", name, val);
}

C# 4.0 dynamic

Ver. 4.0

  • C# 4.0 から、遅延バインドがやりやすくなった。
  • C# 4.0 / .NET Framework 4.0 の売りの1つ。COM Interop の強化。
  • dynamic キーワード。
  • CreateInstance までは C# 3.0 以前と一緒。 残りの部分は var が dynamic に変わったくらいで、事前バインド版とほぼ一緒。
  • No PIA。 .NET Framework 4.0 の新機能。 必要なメソッドの分だけ Runtime Callable Wrapper 作るみたい。
  • 名前付き引数・デフォルト引数と併せることで、Type.Missing 地獄から解放される。
// DOMDocument60Class
Type t = Type.GetTypeFromCLSID(
  new Guid("88D96A05-F192-11D4-A65F-0040963251E5"));

dynamic doc = Activator.CreateInstance(t);
doc.load("sample.xml");

dynamic elem = doc.documentElement();
dynamic items = elem.getElementsByTagName("Item");

foreach (dynamic item in items)
{
    var name = item.getAttribute("Name") as string;
    var val = item.getAttribute("Value") as string;

    Console.WriteLine("{0} = {1}", name, val);
}

サンプル

キーロガー

[お問い合わせ](q)   ぷちカンパ