lisz-works

技術と興味の集合体

C言語の構造体→C#の構造体にコピー!「構造によっては」できるが…

【スポンサーリンク】

 プログラムのソースコード

C言語の構造体を、C#の構造体へ取り込めないか試してみました。

やりたいこと

このようなことができないか試みました。

  1. C言語とC#で同じ構成の構造体を作成する。
  2. C言語側で値の設定して出力
  3. C#側で出力したデータを読み込む
  4. 読み込んだデータをC#側の構造体にまるごとコピーできないか?

C言語で

構造体作成→バイナリファイル出力→読込→構造体にまるごとセット

とするとできるので、「C言語→C#でこれができないか?」と思い試した感じです。

C言語の構造体を準備する

まずは出力するC言語の構造体の作成です。
これらのことをしていきます。

  1. ある構造体を定義
  2. 値を入れる
  3. バイナリ出力

構造体定義

適当な構造体を作成します。
念の為、キリの良いバイト数の並びにしています。

// 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()」です。

流れとしては

  1. Cで出力したファイルを読込バイト列データを作成
  2. 構造体を生成して、バイト列をコピー

このコピー処理が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してあげる必要があります。

ということは、宣言→コピーということができません……残念……

参考

こちらを参考にしました。ありがとうございました!

note.websmil.com

あとがき

ということで、ぼくのやりたかったことは、「できたけどできない」という結果に終わりました。

とはいえ、単純な構造体では実践できるので、何かあったら使えるかもしれませんね(あまり使う機会はないでしょうが)。