将C#结构与流式Delphi记录进行1:1对齐可能吗? [英] 1:1 Alignment of C# Structs with Streamed Delphi Records Possible?

查看:97
本文介绍了将C#结构与流式Delphi记录进行1:1对齐可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个Delphi应用程序,它正在记录各种类型的记录,并通过stream.Write(record, sizeof(record))将它们扔到内存流中,并通过命名管道发送它们.

So I have a Delphi application that is taking records of various types, throwing them on a memorystream via stream.Write(record, sizeof(record)) and sending them over a named pipe.

获取此Delphi记录:

Take this Delphi record :

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

当它通过命名管道发送时,它像这样在byte []数组中出现:

When this is sent over the named pipe, it comes out like this in a byte[] array :

长度:22字节

0x06、0x00、0x00、0x00、4个字节用于数组

0x06, 0x00, 0x00, 0x00, 4 bytes for array

0x00、0x00、0x00、0x00,用于构建的2个字节,用于对齐的2个字节?

0x00, 0x00, 0x00, 0x00, 2 bytes for build, 2 bytes for alignment?

0x15、0xA3、0x86、0x3F,8个双字节字节

0x15, 0xA3, 0x86, 0x3F, 8 bytes for double

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

0xBC, 0x44, 0xE4, 0x40,

0xA3、0x02、0x00、0x00、2个字节用于SVNChangeSet,2个字节对​​齐?

0xA3, 0x02, 0x00, 0x00, 2 bytes for SVNChangeSet, 2 bytes alignment?

0x00、0x00,还有2个字节用于其他内容?

0x00, 0x00, 2 bytes for something else?

对齐问题

  1. 我相信这被称为4字节边界对齐,对吗?
  2. 最后两个字节是做什么用的?

现在,我正在尝试(失败)将其编组为C#结构.

Now I'm trying (unsuccessfully) to marshal this into a C# struct.

    [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的倍数(或者我不记得确切是8).

Alignment is normally used by the compiler as an optimization. Actually every structure is padded to a multiple of 4 (or 8 I don't remember exactly).

更新:我上面提到的有关对齐的内容不准确.阅读David的答案,以获取有关编译器如何处理对齐记录的详细信息. Wikipedia文章包含合理的概述: http://en.wikipedia.org/wiki/Data_structure_alignment

Update: What I state above about alignment is not accurate. Have a read of David's answer for details on how aligned records are treated by the compiler. The Wikipedia article contains a reasonable overview: http://en.wikipedia.org/wiki/Data_structure_alignment

无论如何,您可以使用Pack参数指定对齐方式. Pack值为1会返回结构的确切大小.

Anyway, you can use the Pack parameter to specify the alignment. A Pack value of 1 returns the exact size of the structure.

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

关于数组,正确使用:

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

当您从结构转换为字节数组时,请务必注意该数组必须与声明的大小匹配.转换之前,如果内容较短,则应使用 Array.Resize .

Just take care when you convert from structure to byte array that the array must match the declared size. Before converting, you should use Array.Resize if the content is shorter.

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

关于封送处理,我使用了从byte []到结构的这种方法:

About marshaling I use this method from byte[] to structure:

    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;
 }

此方法的作用与此相反:

And this method does the opposite:

    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;
    }

另外,请使用以下代码检查框架计算出的大小:

Also, please use the following code to check the size calculated by the framework:

int objectSize = Marshal.SizeOf(structure);

最后,我发现有关编组的这一很好的文章.

Finally I found this nice article about marshalling.

这篇关于将C#结构与流式Delphi记录进行1:1对齐可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆