渋谷ほととぎす通信

新しいこと、枯れたこと問わず大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ

C#からC++(DLL)に構造体を渡す その1


VisualC++で作成したDLLに対して、C#から構造体を渡して返してもらうということをやってみます。

まずは手始めに変数が値型のみの構造体からやってみます。

環境周りの話は以前の記事を参考にどうぞ。 www.shibuya24.info

今回作るもの

public struct StructData
{
    public int id;
    public float value;
}

↑のC#で定義した構造体をC++側に渡して、C++内で、値を加工して同じ構造体で返してみるというものを作ってみます。

C++側でC#と同様の構造体のポインタを定義

typedef struct 
{
    int id;
    float value;
}StructData;

C++側でも同様の構造体を定義します。

DllExport void TestStruct(StructData* output, StructData* input);

TestStructメソッドを定義します。 引数には、C++からC#に戻すoutput、C#からC++に渡される引数をinputと定義しておきます。どちらもポインタ型です。

void TestStruct(StructData* output, StructData* input)
{
    output->id = input->id + 20;
    output->value = input->value +0.9999;
}

TestStructの実装自体はシンプルで、inputから渡された値に対して定数を加算してoutputに代入します。

C#側からはポインタで渡して構造体で返ってくる

C#側の実装に移ります。

[DllImport("TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void TestStruct(ref StructData output, IntPtr input);

C#からはinputにStructDataのポインタを渡し、戻ってくるデータはStructDataそのものが返ってきます。

// アンマネージドの構造体のメモリ確保
IntPtr inputPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(StructData)));

ポインタ生成(メモリ確保)方法は前回記事のマーシャリングと同じで、上記のMarshal.AllocCoTaskMem関数を使用します。

C#からC++(DLL)に配列を渡す - 渋谷ほととぎす通信

アンマネージドメモリに構造体をコピー

// マネージド構造体をアンマネージドにコピーする
Marshal.StructureToPtr(inputData, inputPtr, false);

前回は配列をコピーする上でMarshal.Copy関数を使用しましたが、今回は構造体なのでMarshal.StructureToPtr関数を使用します。

C++を呼ぶ

// C++から戻す結果構造体
var result = new StructData();
TestStruct(ref result, inputPtr);

このように第一引数のresultにC++が書き換えた構造体の参照が入って完了です。

まとめ

今回はシンプルな構造体で、とても簡単でした。
次回はもっと複雑な構造体をやってみます。

今回のサンプルコードはこちら。

その他

本日のイチロー引退会見を見て、自分を成長させるためには、少しずつ以前の自分を超えていくしかないと改めて感じた次第です。

参考