C言語の構造体を、C#の構造体へ取り込めないか試してみました。
やりたいこと
このようなことができないか試みました。
- C言語とC#で同じ構成の構造体を作成する。
- C言語側で値の設定して出力
- C#側で出力したデータを読み込む
- 読み込んだデータをC#側の構造体にまるごとコピーできないか?
C言語で
構造体作成→バイナリファイル出力→読込→構造体にまるごとセット
とするとできるので、「C言語→C#でこれができないか?」と思い試した感じです。
C言語の構造体を準備する
まずは出力するC言語の構造体の作成です。
これらのことをしていきます。
- ある構造体を定義
- 値を入れる
- バイナリ出力
構造体定義
適当な構造体を作成します。
念の為、キリの良いバイト数の並びにしています。
// str.h #ifndef __STR_H__ #define __STR_H__ typedef struct _TBL { double d1; double d2; double d3; short s1; short s2; short s3; short s4; } TBL; #endif
出力ソース
構造体を生成、値のセットをし、バイナリファイルに出力するソースです。
#include <stdio.h> #include "str.h" #define _CRT_SECURE_NO_WARNINGS #define OUTPATH "<出力先パス>/out.bin" int main(void) { TBL t = { 1.1, 2.2, 3.3, 4, 5, 6, 7 }; FILE* fp; if ( (fp = fopen(OUTPATH, "wb")) == NULL ) { return 1; } fwrite(&t, sizeof(t), 1, fp); fclose(fp); return 0; }
C#で読み込む
それではC#側のソースです。
構造体の定義
C#側での構造体定義波こんな感じ。
変数の並びをC言語側と同じにしてあります。
struct TBL { double d1; double d2; double d3; short s1; short s2; short s3; short s4; };
読込→セットするロジック
こちらの参考にしたページ に、2種類やり方が書いてあったので、両方試してみました。
実際に処理の大枠を行うのは「Exec()」です。
流れとしては
- Cで出力したファイルを読込バイト列データを作成
- 構造体を生成して、バイト列をコピー
このコピー処理が2種類あったので、2つの関数に分けて記述しています。
// Marshalを使ったコピー static TBL CopyMarshal(byte[] buf) { int size = Marshal.SizeOf(new TBL()); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(buf, 0, ptr, size); TBL tbl = (TBL)Marshal.PtrToStructure(ptr, typeof(TBL)); Marshal.FreeHGlobal(ptr); return tbl; } // GCHandleを使ったコピー static TBL CopyGCHandle(byte[] buf) { int size = Marshal.SizeOf(new TBL()); IntPtr ptr = Marshal.AllocHGlobal(size); GCHandle gch = GCHandle.Alloc(buf, GCHandleType.Pinned); TBL tbl = (TBL)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(TBL)); gch.Free(); return tbl; } // 実行 static void Exec() { // ファイル読込 FileStream fs = new FileStream( @"<パス>\out.bin", FileMode.Open, FileAccess.Read); int fileSize = (int)fs.Length; byte[] buf = new byte[fileSize]; int readSize = fs.Read(buf, 0, 32); fs.Dispose(); // 読み込んだデータをコピー TBL tblM, tblG; // Marshalでコピー tblM = CopyMarshal(buf); // GCHandleでコピー tblG = CopyGCHandle(buf); return; }
結果
ちゃんと値が入ってます!
このようなロジックで、C→C#への構造体データのコピーができることがわかりました!
構造体に配列を含む場合
喜んだのもつかの間、試していったら問題点がありました。
構造体に配列を含む場合は、この方法は使えない。
ということです。
今回の例のような、フラットに変数を並べた構造体はいいんですが、配列が含まれるとそうはいかなくなってきます。
そもそもC#での構造体は、宣言で配列要素数が定義できません(知らなかった)。
なので、初期化などで配列部分に、要素数分の配列変数をnewしてあげる必要があります。
ということは、宣言→コピーということができません……残念……
参考
こちらを参考にしました。ありがとうございました!
あとがき
ということで、ぼくのやりたかったことは、「できたけどできない」という結果に終わりました。
とはいえ、単純な構造体では実践できるので、何かあったら使えるかもしれませんね(あまり使う機会はないでしょうが)。