1

A binary file has the following layout:

[FileIdentifier][HeaderStruct][ItemStruct]*[MuchBinaryData]

The [HeaderStruct] contains a Count field, indicating how many [ItemStruct] records follow. Without analyzing this data, I can't access [MuchBinaryData].

The fixed-length structures look as follows:

Public Structure HeaderStruct
    Public MajorVersion As Integer
    Public MinorVersion As Integer
    Public Count As Integer         'Number of contained ItemStruct.
End Structure

Public Structure ItemStruct
    Public ItemType As Type              
    Public Length As Long 
    ...          
End Structure

Retrieving the [FileIdentifier] field is trivial, it's just a field of 6 bytes.

Public Sub New(sFileName As String)
    Dim abFileID(0 To FILEIDENTIFIER.Length - 1) As Byte
    If File.Exists(gsFileName) Then
        Using oFS = File.Open(sFileName, FileMode.Open, FileAccess.Read)
            oFS.Read(abFileID, 0, abFileID.Length)
            ...
        End Using
    End If
End Sub

So my question is: how can I retrieve these (variable number) of structures as, well, structures?

Do I really need to access each byte, compose a value with lower Endian in mind, and manually assign the calculated value to the structures' fields?

Edit

I found this suggestion using GCHandle and IntPtr: Is there a way to convert a structure to a byte array?

(It definitely needs being wrapped.)

Is this the best I can hope for?

braX
  • 11,506
  • 5
  • 20
  • 33
  • The `GCHandle` solution should work quite well as you can do it both ways (struct -> bytes and bytes -> struct). Here's a C# sample: https://stackoverflow.com/a/2887 – Visual Vincent Apr 19 '18 at 12:53
  • @VisualVincent, thanks, I'm trying to deal with the GCHandle, but it causes head-aches: https://stackoverflow.com/questions/49934468/marshal-copy-not-copying-from-bytes-array-into-structure-starting-at-the-address –  Apr 20 '18 at 08:15

1 Answers1

0

To copy a bytes array into a structure record in general:

'Marshal, GCHandle, GCHandleType:
Imports System.Runtime.InteropServices

'Copies a bytes array into a structure record.
Public Shared Sub BytesToStructure(Of T As Structure)(abBytes As Byte(),
    ByRef oStruct As T)

    Dim hBytes As GCHandle
    Dim iBytes As IntPtr

    'Obtain a handle to the byte array, pinning it so that the garbage
    'collector does not move the object. This allows the address of the 
    'pinned structure to be taken. Requires the use of Free when done.
    hBytes = GCHandle.Alloc(abBytes, GCHandleType.Pinned)
    Try
        'Obtain the byte array's address.
        iBytes = hBytes.AddrOfPinnedObject()

        'Copy the byte array into the record.
        oStruct = Marshal.PtrToStructure(Of T)(iBytes)
    Finally
        hBytes.Free()
    End Try
End Sub

One practical application (and the answer to the question) is to read structure records, which were written into a file back into such structure records.

'Expects to find a structure of type T at the actual position of the 
'specified open filestream oFS. Reads this structure and copies
'it to the structure variable specified in oStruct.
Public Shared Sub ReadBytesToStructure(Of T As Structure) _
    (oFS As FileStream, ByRef oStruct As T)

    Dim iStructLen As Integer
    Dim abStreamData As Byte()

    'From the file's current position, read the number of bytes required to
    'fill the structure, into the byte array abStreamData.
    iStructLen = Marshal.SizeOf(oStruct)
    ReDim abStreamData(0 To iStructLen - 1)
    oFS.Read(abStreamData, 0, iStructLen)

    'Copy the read bytes into the provided record.
    BytesToStructure(Of T)(abStreamData, oStruct)
End Sub