所以我有一个Delphi应用程序正在记录各种类型的记录,通过stream.Write(record, sizeof(record))
将它们放在内存流上并通过命名管道发送。
拿这个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,双字节为8个字节
0xBC,0x44,0xE4,0x40,
0xA3,0x02,0x00,0x00,2个字节用于SVNChangeSet,2个字节对齐?
0x00,0x00,2个字节用于别的什么?
对齐问题
现在我正在尝试(不成功)将其编组为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#问题
答案 0 :(得分: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);
关于编组我使用这个方法从byte []到结构:
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);
最后我发现这个很好article关于编组。
答案 1 :(得分:2)
我将假设您的Delphi编译器在默认模式下运行,因此正在调整记录。我还假设您的Pascal代码包含一个简单的拼写错误,其中您的数组有5个元素而不是4个。
对齐记录的对齐方式由具有最大对齐的成员确定。最大的成员是8字节双。因此记录具有对齐8.其大小是其对齐的精确倍数。
每个单独的字段都对齐以匹配字段的对齐方式。字节数组具有对齐1并且可以出现在任何位置。 2字节整数必须出现在2字节边界上,依此类推。记录末尾可能有填充以确保记录的数组也将对齐。因此记录的大小是8的倍数。您的记录大小为24。
在您的情况下,填充在8字节双精度之前,并在记录的末尾。如果未包含末尾的填充,则8字节双精度值将在您的记录的任何数组中错位。
您无需对Delphi记录和C#struct声明进行任何更改。它们已被正确声明,并且完美匹配。
您可以使用Delphi中的SizeOf和C#中的Marshal.SizeOf来检查结构的大小。您会发现它们与问题中的代码匹配。
我无法评论您的代码是如何失败的,因为您没有描述该失败。我的主要观点是,任何失败都不是因为你的结构之间的不匹配。对于数字22的来源,没有合理的解释。我会查看该数字的来源。
最后,您接受的答案建议使用压缩结构。没有必要这样做,我认为没有理由这样做,也没有解释你的问题。