【问题标题】:Proper struct layout from delphi packed record来自delphi打包记录的正确结构布局
【发布时间】:2016-03-03 17:52:17
【问题描述】:

我正在将一个 delphi 应用程序转换为 C#。有一堆打包的记录,根据我几周前问的一个类似问题,最好转换为类。但是,有人告诉我需要将它们转换为结构,我可以使用一些帮助。我将使用BinaryReader 从文件中读取并将值分配给结构内的字段。

*注意,我正在读取的文件是使用 Delphi 和打包记录制作的。

这是一个示例结构:

德尔福:

Testrec = packed record
    now: TDateTime;
    MinLat: longint;
    MinLong: longint;
    Firsttime: TDateTime;
    MinAlt: single;
    MinFirst: single;
    MinDepth: single;
    MinSpeed: single;
    MinBot: single;
    res3: single;
    res4: single;
    res5: single;
    res6: single;
    MaxLat: longint;
    MaxLong: longint;
    Lasttime: TDateTime;
    MaxAlt: single;
    MaxFirst: single;
    MaxDepth: single;
    MaxSpeed: single;
    MaxBot: single;
    res9: single;
    res10: single;
    res11: single;
    res12: single;
    DataFlags: longint;
    ReviewFlags: longint;
    res13: longint;
    FirstPost: longint;
end;

这是我的 C# 版本:

public struct Testrec
{
    double now;
    int MinLat;
    int MinLong;
    double Firsttime;
    float MinAlt;
    float MinFirst;
    float MinDepth;
    float MinSpeed;
    float MinBot;
    float res3;
    float res4;
    float res5;
    float res6;
    int MaxLat;
    int MaxLong;
    double Lasttime;
    float MaxAlt;
    float MaxFirst;
    float MaxDepth;
    float MaxSpeed;
    float MaxBot;
    float res9;
    float res10;
    float res11;
    float res12;
    int DataFlags;
    int ReviewFlags;
    int res13;
    int FirstPost;
 }

我需要做一个StructLayoutSizeCharSet吗?

编辑:这是有关读取二进制文件的相关delphi代码:

Testrec Header;
HeaderSize = 128;

RampStream:=TFileStream.Create(FilePath,fmOpenReadWrite OR fmShareExclusive );

RampStream.Read(Header,HeaderSize);
StartTime:=Header.Firsttime;
EndTime:=Header.Lasttime;

以下是我设置二进制阅读器的方法:

RampStream = new BinaryReader(new FileStream(RampName, FileMode.Open, FileAccess.ReadWrite, FileShare.None));

【问题讨论】:

  • 你的结构超过了推荐的 16 个字节:stackoverflow.com/questions/1082311/…
  • 该结构可能超过了 16 个字节的限制,但这并不能回答问题,也不相关。他的问题主要是如何在 C# 中声明结构,使其不包含填充字节,即具有 bte 对齐。 FWIW,我会以任何语言传递这样的结构作为参考。
  • @RudyVelthuis 作为引用传递意味着被调用者可以修改它。
  • 是的,在 C# 中确实如此。 C# 不能有 const 参数有点问题。但如果速度是一个问题,我仍然会通过参考。

标签: c# delphi pinvoke


【解决方案1】:

您需要指定顺序布局和包装值 1。

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

由于没有文本成员,您无需指定CharSet。你应该让编译器计算结构的大小。现在,指定这个之后,您将能够将整个记录读入内存,然后直接将其 blit 到这个 C# 结构上。像这样:

Testrec ReadRecFromStream(Stream stream)
{
    byte[] buffer = new byte[Marshal.SizeOf(typeof(Testrec))];
    stream.Read(buffer, 0, buffer.Length);
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    try
    {
        return (Testrec)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Testrec));
    }
    finally
    {
        handle.Free();
    }
}

但是,您说您将一次读取成员并分配给 C# 结构中的相应字段。在这种情况下,无需寻求二进制布局等效性,因为您不会使用它。如果您要一次读取一个成员,则不需要StructLayout 属性。您不需要声明任何未使用的成员。您可以在输入时将 Delphi 日期时间值转换为适当的 C# 数据类型等等。

因此,您确实需要决定是否要寻求这些结构的二进制布局等效性。

【讨论】:

  • 谢谢。我将使用有关从文件读取的 Delphi 代码编辑我的帖子。它是一个正在读入的二进制文件。
  • 您可以在 C# 中执行与在 Delphi 中相同的操作来读取文件。你会分配一些非托管内存。你会从文件中读入那个内存。你会使用Marshal.PtrToStructure 来反序列化。仅当您确保二进制布局匹配时,这才会起作用。如果您使用 BinaryReader 逐字段读取,则 C# 结构布局并不重要。而且我绝对认为这里的课程更好。为什么要指示您使用结构?
  • 坦率地说,您需要决定如何处理该文件的读取。您是否要像在 Delphi 中那样进行二进制 blit。还是要逐个字段阅读?
  • 我可以尝试说服他们使用课程。我被告知要使用 struct,因为将有许多结构从文件(数千个)中获取数据,并且据说它会更快。
  • 一旦你开始在函数之间传递它们,它不会更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
  • 2021-05-29
  • 1970-01-01
相关资源
最近更新 更多