異なる構造体間のコピー

| コメント(3) | トラックバック(0)

構造体とバイト配列間のコピーはいくつかサンプルが見つかったのだけど、サイズが同じで異なる構造体間のコピー、というのがなかったので考えてみた。

こんなのはどうだろうか?

[C# Code]

static class Program
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct AAA  // 8byte
    {
        public short v1;    // 2byte
        public short v2;    // 2byte
        public int v34;     // 4byte
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct BBB  // 8byte
    {
        public int v12;     // 4byte
        public ushort v3;   // 2byte
        public byte v4u;    // 1byte
        public byte v4l;    // 1byte
    }

    /// <summary>
    /// Entry point
    /// </summary>
    [STAThread]
    static void Main()
    {
        AAA src = new AAA();
        src.v1 = 0x1234;
        src.v2 = 0x5678;
        src.v34 = 0x11223344;

        // src dump is [34 12 78 56 44 33 22 11]

        BBB dest;

        GCHandle hsrc = GCHandle.Alloc(src, GCHandleType.Pinned);
        dest = (BBB)Marshal.PtrToStructure(hsrc.AddrOfPinnedObject(), typeof(BBB));
        hsrc.Free();

        Console.WriteLine("AAA values");
        Console.WriteLine("{0}-{1}-{2}",
            src.v1.ToString("X4"),
            src.v2.ToString("X4"),
            src.v34.ToString("X8"));

        Console.WriteLine("BBB values");
        Console.WriteLine("{0}-{1}-{2}-{3}",
            dest.v12.ToString("X8"),
            dest.v3.ToString("X4"),
            dest.v4u.ToString("X2"),
            dest.v4l.ToString("X2"));
    }
}

[VB Code]

Public Class Program

    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Structure AAA   ' 8byte
        Public v1 As Short      ' 2byte
        Public v2 As Short      ' 2byte
        Public v34 As Integer   ' 4byte
    End Structure

    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Structure BBB   ' 8byte
        Public v12 As Integer   ' 4byte
        Public v3 As UShort     ' 2byte
        Public v4u As Byte      ' 1byte
        Public v4l As Byte      ' 1byte
    End Structure

    ''' <summary>
    ''' Entry Point
    ''' </summary>
    Public Shared Sub Main()
        Dim src As AAA
        src.v1 = &H1234
        src.v2 = &H5678
        src.v34 = &H11223344

        ' src dump is [34 12 78 56 44 33 22 11]

        Dim dest As BBB

        Dim hsrc As GCHandle = GCHandle.Alloc(src, GCHandleType.Pinned)
        dest = CType(Marshal.PtrToStructure(hsrc.AddrOfPinnedObject(), _
                     GetType(BBB)), BBB)
        hsrc.Free()

        Console.WriteLine("AAA values")
        Console.WriteLine("{0}-{1}-{2}", _
            src.v1.ToString("X4"), _
            src.v2.ToString("X4"), _
            src.v34.ToString("X8"))

        Console.WriteLine("BBB values")
        Console.WriteLine("{0}-{1}-{2}-{3}", _
            dest.v12.ToString("X8"), _
            dest.v3.ToString("X4"), _
            dest.v4u.ToString("X2"), _
            dest.v4l.ToString("X2"))
    End Sub
End Class

ほとんどが定義とコンソール出力だが、実際に構造体のコピーを行っているのはGCHandleから続く3行のみ。GCHandleでポインタを取り、PtrToStructureでポインタのデータを構造体BBBの変数へ格納しているだけ。これを実行すると次のような出力結果が得られる。

AAA values
1234-5678-11223344
BBB values
56781234-3344-22-11

バイト単位で順序が入れ替わっているのはリトルエンディアンの影響。コード中に記したsrcのdumpと見比べてみればちゃんとコピーされているのが分かる。

 

まぁ、「サイズが同じで異なる構造体」でコピーするような場面なんて、そうそうないだろうけども・・・。

トラックバック(0)

トラックバックURL: /mt/mt-tb.cgi/25

コメント(3)

これはよくある処理。
Marshal.PtrToStructure使うと一部の型で不整合出るときがあるので注意。

> 通りすがり様
コメントありがとうございます。

よくある処理なんですね・・・。

> Marshal.PtrToStructure使うと一部の型で不整合出るときがある

「PtrToStructureは使わずバイト配列からひとつずつ取り出して代入したほうがよい」的な記事をどこかで読んだ記憶があります。CopyMemory感覚というわけにはなかなかいきませんね。

ですね。特に、古い時代のライブラリや仕様に準拠している構造体の複製には注意が必要です。機種依存の部分はC#では弱い部分になるので、Marshal.WriteInt16やMarshal.WriteInt32等でバイト内部の配置を変換してあげる必要がある場合もあります。