渋谷ほととぎす通信

完全趣味でやってる技術メモ。※所属団体とは一切関係がありません。

C#からC++(DLL)に配列を渡す


しばらくこのC#からC++に渡すシリーズは続きます。
文字列、int型と来て、今回は配列を渡します。

ビルド環境については、シリーズ初回のこちらの記事をどうぞ。 www.shibuya24.info

今回配列をC#からC++に引き渡す上で重要になるのはマーシャリングです。
マーシャリングとは異なるシステム間のデータ変換を意味します。今回の場合はC++とC#間のデータの変換がそれにあたります。

マーシャリングとは - コトバンク

今までやったint型がマーシャリング処理をしなかったのは、int型がBlittable型でマーシャリングを必要としなかったためです。
文字列は非Blittable型ですが、C#側からは文字列のポインターを渡すだけで済み、明示的な変換処理は不要です。

小ネタ string型のマーシャリング | ++C++; // 未確認飛行 C ブログ

しかし、今回やってみる配列は非Blittable型で、文字列のようにそのままネイテイブに渡すことはできないため、マーシャリングが必要になります。

C++側の処理

マーシャリングで盛り上がりましたが、まずはDLL側の処理を見ていきます。 今回はTestIntArrayというメソッドでC#から配列を受け取ります(厳密には配列のポインタ)。

引数を2つ用意し、配列のポインタと要素の長さを渡せるようにします。

gist.github.com

C++側は特別なことはありません。

C#側の処理でマーシャリング

C#側の処理でマーシャリングしていきます。

  1. C++に渡すC#側の配列のポインタを取得しアンマネージド配列(C++側の配列)のメモリを確保
  2. マネージド配列(C#側の配列)の内容を先程確保したアンマネージドメモリにコピー
  3. アンマネージドC++側のメソッドを実行する時にポインタを渡す

という流れになります。

まずはコードから。 gist.github.com C++側にポインタを渡す際は、C#側ではIntPtr型を使用します。

System.IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * Length);
Marshal.Copy(array, 0, ptr, Length);

重要なのは上の2行で、まさにマーシャリングを行っているコードです。

Marshal.AllocCoTaskMem(メモリ確保するサイズ)で、今回必要とするアンマネージドメモリを確保します。 Marshal.Copy(array, 0, ptr, Length)で、マネージド配列(array)をアンマネージドメモリにコピーし、C++C#間の値の変換をしたことになります。

このC#を実行すると無事に配列の中身をログ出力し、正常に処理が走りました。

まとめ

int型、文字列の引き渡しは簡単でしたが、配列はマーシャリング処理が必要となり少し難易度が上がりましたが、アンマネージドメモリを確保、データのコピーをしてC++側にポインタで引き渡す一連の流れがイメージできると、そんなに難しくは無い気がします。
使い終わったアンマネージドのメモリ解放も忘れずに。

次回は構造体のやり取りをしてみます。


以上