1

stream.Write(record, sizeof(record))そのため、さまざまなタイプのレコードを取得し、名前付きパイプを介してメモリストリームにスローし、送信する Delphi アプリケーションがあります。

この Delphi レコードを取得します。

Type TAboutData = record
  Version : array[0..4] of Byte;
  Build : Word;
  BuildDate : TDateTime;
  SVNChangeset : Word;
end;

これが名前付きパイプを介して送信されると、次のように byte[] 配列に出力されます。

長さ: 22 バイト

0x06、0x00、0x00、0x00、配列用の4バイト

0x00、0x00、0x00、0x00、ビルド用に 2 バイト、アライメント用に 2 バイト?

0x15、0xA3、0x86、0x3F、double の場合は 8 バイト

0xBC、0x44、0xE4、0x40、

0xA3、0x02、0x00、0x00、SVNChangeSet に 2 バイト、アライメントに 2 バイト?

0x00、0x00、2 バイトは別の何かに?

アライメントに関する質問

  1. これは 4 バイト境界でのアラインメントと呼ばれるものだと思いますよね?
  2. 最後の 2 バイトは何ですか?

今、これを C# 構造体にマーシャリングしようとしています (失敗しました)。

    [StructLayout(LayoutKind.Sequential)]
    struct TAboutInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] Version;
        public ushort Build;
        public double BuildDate;
        public ushort SVNChangeSet;
    }    

    IntPtr ptr = Marshal.AllocHGlobal(bytebuffer.Length);
    Marshal.Copy(ptr, bytebuffer, 0, bytebuffer.Length);
    TAboutInfo ta = (TAboutInfo)Marshal.PtrToStructure(ptr, typeof(TAboutInfo));
    Marshal.FreeHGlobal(ptr);

C# に関する質問

  1. これはまったく機能せず、アライメントを説明する方法がわかりません。明示的なオフセットを試しましたが、不足しています。
  2. 多くのレコード タイプがあり、他のレコードの動的配列であるメンバーを持つものもあります。これらのバイト配列を構造体またはオブジェクトに変換するための堅牢なソリューションを思いつきたいと思います。
4

2 に答える 2

3

アラインメントは通常、コンパイラによって最適化として使用されます。実際には、すべての構造が 4 の倍数 (正確には覚えていない 8) にパディングされます。

更新: アラインメントについて上で述べたことは正確ではありません。アラインされたレコードがコンパイラによってどのように処理されるかについての詳細は、David の回答をお読みください。ウィキペディアの記事には、妥当な概要が含まれています: http://en.wikipedia.org/wiki/Data_structure_alignment

いずれにせよ、Pack パラメータを使用して配置を指定できます。Pack 値 1 は、構造体の正確なサイズを返します。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

配列については、使用するのが正しいです:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] someCharArray;

構造体からバイト配列に変換するときは、配列が宣言されたサイズと一致する必要があることに注意してください。変換する前に、コンテンツが短い場合はArray.Resizeを使用する必要があります。

struct.someCharArray = "Hello".ToCharArray();
Array.Resize<char>(ref struct.someCharArray, 20);

マーシャリングについて、バイト[]から構造体までこのメソッドを使用します。

    public static T ParseStructure<T>(byte[] array, int offset) where T : struct
    {
        if (array == null) throw new ArgumentNullException("array", "Input parameter cannot be null");

        if (array.Length - offset < Marshal.SizeOf(typeof(T)))
            Array.Resize<byte>(ref array, Marshal.SizeOf(typeof(T)) + offset);

        int arraySize = array.Length - offset;
        T returnItem;

        // Allocate some unmanaged memory.
        IntPtr buffer = Marshal.AllocHGlobal(arraySize);

        // Copy the read byte array (byte[]) into the unmanaged memory block.
        Marshal.Copy(array, offset, buffer, arraySize);

        // Marshal the unmanaged memory block to a structure.
        returnItem = (T)Marshal.PtrToStructure(buffer, typeof(T));

        // Free the unmanaged memory block.
        Marshal.FreeHGlobal(buffer);

        return returnItem;
 }

そして、このメソッドは反対のことを行います:

    public static byte[] StructureToArray<T>(T structure) where T : struct
    {
        int objectSize = Marshal.SizeOf(structure);
        byte[] result = new byte[objectSize];
        IntPtr buffer = Marshal.AllocHGlobal(objectSize);

        object dataStructure = (object)structure;

        Marshal.StructureToPtr(dataStructure, buffer, true);

        Marshal.Copy(buffer, result, 0, objectSize);
        Marshal.FreeHGlobal(buffer);
        return result;
    }

また、次のコードを使用して、フレームワークによって計算されたサイズを確認してください。

int objectSize = Marshal.SizeOf(structure);

最後に、マーシャリングに関するこの素晴らしい記事を見つけました。

于 2013-08-31T21:06:46.530 に答える
2

ここでは、Delphi コンパイラがデフォルト モードで動作していて、レコードを整列していると仮定します。また、あなたの Pascal コードには、配列の要素が 4 つではなく 5 つある単純なタイプミスが含まれていると思います。

アラインされたレコードのアラインメントは、最大のアラインメントを持つメンバーによって決定されます。最大のメンバーは 8 バイトの double です。そのため、レコードのアラインメントは 8 です。そのサイズは、アラインメントの正確な倍数です。

個々のフィールドは、フィールドの配置と一致するように配置されます。バイト配列のアラインメントは 1 で、どこにでも表示できます。2 バイトの整数は、2 バイトの境界上に表示される必要があります。レコードの配列も整列されるように、レコードの最後にパディングが含まれる場合があります。したがって、レコードのサイズは 8 の倍数です。レコードのサイズは 24 です。

あなたの場合、パディングは8バイトのダブルの前で、レコードの最後にあります。末尾のこのパディングが含まれていない場合、8 バイトの double は、レコードの配列内で不整列になります。

Delphi レコードと C# 構造体宣言を変更する必要はありません。それらはすでに正しく宣言されており、互いに完全に一致しています。

Delphi では SizeOf を、C# では Marshal.SizeOf を使用して、構造体のサイズを確認できます。質問のコードと一致することがわかります。

あなたがその失敗を説明していないので、あなたのコードがどのように失敗したかについてコメントすることはできません. 私の主なポイントは、失敗は構造間の不一致によるものではないということです。22 という数字の出所についての合理的な説明はありません。その数字がどこから来たのかを調べたいと思います。

最後に、あなたが受け入れた答えは、パック構造を使用することを提案しています。そうする必要はありません。そうする理由がわかりません。また、問題を説明していません。

于 2013-09-01T06:38:22.003 に答える