C# へのネイティブ DLL の取り込み - 構造体編

ここまでで,C# から C で作成した DLL のメソッドへの変数・文字列の渡し方を見てきたが,構造体を引数として渡すことも実用上考えられる.ここでは,構造体を渡す方法をしらべたのでまとめる.

1. C で定義した構造体を C# でも定義する.

[StructLayout(LayoutKind.Explicit)]
struct SAMPLE {
  [FieldOffset(0)]
  public int width;
  [FieldOffset(4)]
  public int height;
  [FieldOffset(8)]
  public byte* buf;
}

上のサンプルでは,"LayoutKind.Explicit" 属性によってメンバの配置を明示的に指定している.この方法をとった場合,各要素のバイト位置を "FieldOffset(x)" で指定しないといけない.

2. C で定義されている関数を C# で呼んでやる.

[DllImport("MyDll.dll", CallingConvention = CallingConvenction.Cdecl)]
unsafe private extern static void FillSample(ImageData *data);

3. C# のコードを書く.

例1

undafe public void test(int *size);


unsafe public void test(int *size) 
{
  byte[] data = new byte[10]
  fixed (byte *p = data) {

    SAMPLE o = new SAMPLE();
    FillSample(&o);

  }

}

例2

public unsafe int Read(byte[] buffer, int index, int count)
{
  int bytesRead = 0;
  fixed (byte* bytePointer = buffer)
  {
    ReadFile(fileHandle, bytePointer + index, count, &bytesRead, 0);
  }
  return bytesRead;
}

ここで,unsafe というキーワードは C# にてポインタを使用する際に必要となるもので,C# では unsafe が宣言されたコンテキストでなければポインタを使うことができない.まとめると....

  • メソッドやブロック内でポインタを使用したいときには,unsafe キーワードをつける.
  • unsafe を使うためには,コンパイル時にコンパイラに明示的に宣言しないといけない.Visual Studio の場合は,プロジェクトのプロパティをひらいて「ビルド」⇒「全般」の「アンセーフコードの許可」にチェックを入れる.

また,unsafe コンテキストでは下記の機能が利用可能になる.

fixed keyword

 最初の例「SAMPLE」を考えると,構造体の中にはポインタ変数 *buf がある.ポインタ変数でアクセスするということはデータの領域が連続していないといけないが,.NETFrameWorkでは効率化を実現するためにガーベッジコレクションのプログラムがバッファの位置を動かしてしまうかもしれない.C の関数では,バッファの位置は当然ながら連続したものとして考えて処理されるであろうから,確保した配列 data に関してすくなくとも C の関数に呼ばれている間は配列の値が同じメモリ状の位置に固定されていないといけない.この機能を提供するのが fixed keyword である.fixed コンテキスト内では,指定されたポインタのメモリ上の位置がコンテキスト内では不変であることが保証される.