1

I'm converting a delphi app to C#. There are a bunch of packed records, and according to a similar question I asked a few weeks ago, it would be better to convert to classes. However, I'm told I need to convert them to structs, and I could use some help. I'm going to be using a BinaryReader to read from a file and assign values to the fields inside of the structs.

*Note, the file I'm reading from was made using Delphi and packed records.

Here's an example struct:

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;

Here's my C# version:

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

Do I need to do a StructLayout, Size, and CharSet?

Edit: Here's the relevant delphi code regarding the reading of the binary file:

Testrec Header;
HeaderSize = 128;

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

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

Here's how I set up my Binary Reader:

RampStream = new BinaryReader(new FileStream(RampName, FileMode.Open, FileAccess.ReadWrite, FileShare.None));
4
  • 1
    Your struct is way past the recommended 16 bytes: stackoverflow.com/questions/1082311/… Commented Mar 4, 2016 at 9:09
  • The struct may be past the 16 byte limit, but that doesn't answer the question nor is it relevant. His question is mainly how to declare the struct in C# such that it contains no filler bytes, i.e.has bte alignment. FWIW, I would pass such a struct as reference, in any language. Commented Mar 4, 2016 at 12:21
  • 1
    @RudyVelthuis Passing as a reference means that the callee can modify it. Commented Mar 4, 2016 at 12:30
  • Yes, in C# that is true. That C# can't have const parameters is a bit of a problem. But if speed is an issue, I would still pass by reference. Commented Mar 4, 2016 at 16:25

1 Answer 1

4

You need to specify sequential layout and a pack value of 1.

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

Since there are not textual members, you need not specify CharSet. And you should let the compiler calculate the size of the struct. Now, having specified this you will be able to read the entire record into memory, and then blit it directly onto this C# struct. Like so:

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

However, you said that you were going to read on member at a time and assign to the corresponding field in the C# struct. In that case there's no need to seek binary layout equivalence since you won't be making any use of that. If you are going read one member at a time then you do not need a StructLayout attribute. You don't need to declare any unused members. You can convert the Delphi date time values into the appropriate C# data types at the point of input and so on.

So, you do need to make up your mind as to whether or not you are going to seek binary layout equivalence of these structures.

Sign up to request clarification or add additional context in comments.

16 Comments

Thank you. I'm going to edit my post with the Delphi code regarding the reading from the file. It is a binary file that's being read in.
You can do the same thing in C# as your do in Delphi to read the file. You'd allocate some unmanaged memory. You'd read into that memory from the file. And you'd use Marshal.PtrToStructure to deserialize. That's going to work only if you make sure that the binary layout matches. If you use instead a BinaryReader to read field by field then the C# struct layout is not important. And I definitely think that a class is better here. Why are you being instructed to use a struct?
Frankly, you need to make a decision on how you are going to handle the reading of this file. Are you going to do a binary blit as your did in Delphi. Or are you going to read field by field?
I can try to convince them to use a class. I was told to use struct since there's going to be many structs being fed data from a file (in the thousands) and that it will supposedly be faster.
It won't be faster once you start passing them around between functions.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.