none
メンバに動的配列をもつ構造体のマーシャリングについて RRS feed

  • 質問

  • いつもお世話になっております。

    メンバに動的配列をもつ構造体のマーシャリングについて、ご教授お願い致します。

    以下のような構造体をCで作成したdllに渡し、再度dll側から取得して、構造体に格納するようなことを行っています。

    [C#]

    public struct Test_Struct
    {
        public int intData1;                  
        public int intData2;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
        public byte[] byteArray;                 
    }

    このような場合は、byte配列が128と固定なので、以下のコードのようにIntPtrに変換し受け渡し可能なのですが、


    Test_Struc structData;
    ~処理~
    IntPtr inputPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Test_Struct)));
    Marshal.StructureToPtr(structData, inputPtr, false);

    byteArrayが可変の場合(128固定ではなく256になる場合もあれば、64にもなる場合がある)、いろいろ調べましたが、基本、「できない!」と回答されている方が多いのですが、何か良い案があればと思い、質問させていただきました。

    Marshal.AllocCoTaskMem を呼び出すときにはbyteArrayのサイズがわかっているので、何とかならないかと模索中です。

    諦めきれず、質問させていただきました。

    申し訳ございませんが、よろしくお願い致します。


    • 編集済み coco2014 2020年3月6日 1:21
    2020年3月6日 1:20

回答

  • 書く方は、 8+byteArray.Lengthで確保して、 Marshal.WriteInt32(ptr, intData1); Marshal.WriteInt32(ptr+4, intData2); Marshal.Copy(byteArray, 0, ptr+8, byteArray.Length); 読む方は、どこかにbyteArrayの長さがあるはずなので、 intData1=Marshal.ReadInt32(ptr); intData2=Marshal.ReadInt32(ptr+4); byteArray=new byte[len]; marshal.Copy( ptr+8, byteArray, 0,len]; で良いのでは?

    jzkey

    • 回答としてマーク coco2014 2020年3月6日 7:16
    2020年3月6日 4:56

すべての返信

  • 受け渡す構造体の定義は、まずC#でやりたい形を決めてからC側で実装するという手順でいいのでしょうか?

    C側の実装が変更できないなら、その実装を前提に話を進める必要がありますが。そうであればC側の構造体定義を示していただく必要もありそうです。

    2020年3月6日 1:25
  • Hongliangさん、返信ありがとうございます。

    既にC側の実装はあります。C側の実装は変更できません。

    ただし、C側の実装は、単純に指定のサイズでメモリを確保して、指定されたバイト配列(バイナリ値)を格納するだけと思ってください。(C側では構造体として定義されていません。)

    従って、C#側で可変のサイズを決めることができます。

    コンパイル時はサイズは決まっていません。動作させる状況に応じてサイズを決めるプログラムです。

    回答になっていますでしょうか?

    お手数をお掛け致しますが、よろしくお願い致します。


    • 編集済み coco2014 2020年3月6日 1:57
    2020年3月6日 1:52
  • unsafeでよいのであれば、fixedフィールドを使えば、C言語的に古式ゆかしい可変長配列の定義をC#でも可能ですね。fixedフィールドは配列そのもの(Array派生クラスという意味で)としては使えませんが。

    unsafe struct Hoge {
        public int BufferSize;
        public fixed byte Buffer[1];
    }
    
    unsafe void DoTest() {
        int size = 16, totalSize = sizeof(int) + size;
        var bytes = new byte[totalSize];
        fixed (byte* pb = bytes) {
            Hoge* hoge = (Hoge*)pb;
            hoge->BufferSize = size;
            for (int i = 0; i < size; i++) {
                hoge->Buffer[i] = i;
            }
        }
        Console.WriteLine(BitConverter.ToString(bytes));
    }
    2020年3月6日 2:24
  • Hongliangさん

    返信ありがとうございます。

    unsafe , fixed ですね!

    unsafeでコードを書いたことは無いので、その使い方、思いつきませんでした。

    unsafeは許されるか確認してみます。

    ありがとうございます。



    • 編集済み coco2014 2020年3月6日 2:50
    2020年3月6日 2:42
  • new byte[]したc#配列と、アンマネージドなIntPtr間を、Marshal.Copyでやりとりすれば良いと思いますが。

    jzkey

    2020年3月6日 3:47
  • jzkeyさん

    返信ありがとうございます。

    その通りなのですが、C#で可変配列を持つ構造体をbyte配列に変換したり、byte配列から可変配列を持つ構造体に変換する方法がわからないのです。

    C#

    public struct Test_Struct
    {
        public int intData1;                  
        public int intData2;
        public byte[] byteArray;                 
    }

    上記byteArrayは可変長のbyte配列です。使うときにはサイズは決まっているのですが。受け取ったbyte配列をこの構造体の内容に変換、またはこの構造体の内容をbyte配列に変換が、

    Test_Struc structData;
    ~処理~
    IntPtr inputPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Test_Struct)));
    Marshal.StructureToPtr(structData, inputPtr, false

    のような形で、不可能なので困っています。

    そもそもこの構造体のbyteArrayを別にして、byte配列をつなげたり分解したらいいのかもしれないと。思うこの頃です。。
    • 編集済み coco2014 2020年3月6日 4:24
    2020年3月6日 4:13
  • 書く方は、 8+byteArray.Lengthで確保して、 Marshal.WriteInt32(ptr, intData1); Marshal.WriteInt32(ptr+4, intData2); Marshal.Copy(byteArray, 0, ptr+8, byteArray.Length); 読む方は、どこかにbyteArrayの長さがあるはずなので、 intData1=Marshal.ReadInt32(ptr); intData2=Marshal.ReadInt32(ptr+4); byteArray=new byte[len]; marshal.Copy( ptr+8, byteArray, 0,len]; で良いのでは?

    jzkey

    • 回答としてマーク coco2014 2020年3月6日 7:16
    2020年3月6日 4:56
  • jzkeyさん

    返信ありがとうございました。

    できました!

    jzkeyさんの案を参考に、構造体のbyteArrayを別にして、byte配列をつなげたり分解したりして、byte配列のでのMarshal.Copyでできました。

    ありがとうございました。大変助かりました!

    2020年3月6日 7:16