Reading a binary file with VB.NET

Let's try this again, a little more concisely:

I am trying to convert some code from VB6.0 to VB.NET, this particular loop populates a data structure from a binary file, and I am having a huge problem getting equivalent results.  I'll cut and paste the old code and new code below, and if anyone can tell me what I'm doing wrong, or (even better!) give me a couple of snippets, I'd be eternally graceful!  

The problems are in the GetVariable function of the second section.  Just pasted the rest for completeness.  Part of the problem this isn't simple is because the BinaryReader doesn't seem to support random access... but I'm not married to it.


old, working VB6.0 code

Private Type XVDataType

    sText As String * 50
    iNumPoints As Integer
    dData(50) As Double

End Type

Private Sub ButtonGo_Click()
     Dim X as XVDataType

     Dim f as String
     f = "C:\Documents and Settings\crice\My Documents\Active Tasks\TCFLO\Project\TCFLO\TCFLO.rst"

     X = GetVariable(f, 4)    ' calls the getvariable function with some dummy data, to test

End Sub

Private Function GetVariable(sRestartFile As String, iNumXVar As Integer) As XVDataType

    ' inumvar is 1-based
    Dim XV As XVDataType

    ' get the variable definitions
    Dim ind As Long
    Open sRestartFile For Binary As #1
    ind = 254  ' header offset

    Get #1, ind + (iNumXVar - 1) * 508 + 1, XV.iNumPoints
    Dim n As Integer
    For n = 0 To XV.iNumPoints - 1
        Get #1, ind + (iNumXVar - 1) * 508 + 9 + n * 8, XV.dData(n)
    Next n
    Get #1, ind + (iNumXVar - 1) * 508 + 459, XV.sText

    GetRestartExternalVariable = XV
    ' close the file and exit
    Close #1
    Exit Function

End Function

new, mostly not working VB.NET code

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click

        Call GetVariable( _
        "C:\Documents and Settings\crice\My Documents\Active Tasks\TCFLO\Project\TCFLO\TCFLO.rst" _
        , 4)

    End Sub

    Private Function GetVariable(ByVal sRestartFile As String, ByVal iNumXVar As Integer) As XVDataType

        Dim XV As New XVDataType

        ' get the variable definitions
        Dim ind As Long = 254   ' header offset

        If System.IO.File.Exists(sRestartFile) Then

            Dim binReader As New System.IO.BinaryReader(File.Open(sRestartFile, FileMode.Open))
            binReader.ReadBytes(ind + (iNumXVar - 1) * 508)
            XV.iNumPoints = binReader.ReadByte()     ' <---- THIS WORKS, gets the right number of points
            For n As Integer = 0 To XV.iNumPoints - 1

                XV.dData(n) = binReader.ReadSingle    ' <---- this doesn't work worth a darn
                'XV.dData(n) = binReader.ReadDouble    '    <------ this didn't work either
                'binReader.ReadBytes(8)                         '    < ------ nor this        
                'Get #1, ind + (iNumXVar - 1) * 508 + 9 + n * 8, XV.dData(n)   ' <------- the equivalent VB6.0 code

            Next n

            XV.sText = binReader.ReadChars(10)      ' <------ doesn't work


        End If

    End Function
Who is Participating?
SanclerConnect With a Mentor Commented:
Your structure definition suggests that the data would be saved in the order sText (String - fixed length 50), then iNumPoints (Integer), then dData (Double * 51).  But your code appears to read the data in the order INumPoints, then dData, then sText.  I'm assuming that it is the latter that is correct.

That iNumPoints is in fact the first data saved in the file structure appears to be confirmed by the fact that your VB.NET code is OK up to this point

            XV.iNumPoints = binReader.ReadByte()     ' <---- THIS WORKS, gets the right number of points

But what you are reading is a Byte whereas what your structure appears to contain is an Integer.  An Integer, depending on its type, will be 2-, 4- or 8-bytes.  What you have read is the first 4 BITS of the data, which happens to give you the right value.  But the BinaryReader's position has only been advanced by one byte whereas the next data does not begin until after the 2-, 4- or 8-byte boundary.  So any subsequent read is going to start at the wrong place.  Which is why, I think, none of your attempts to read the next data worked.  What you need to do first, I think, is replace

            XV.iNumPoints = binReader.ReadByte()


            XV.iNumPoints = binReader.ReadInt16()
            XV.iNumPoints = binReader.ReadInt32()
            XV.iNumPoints = binReader.ReadInt64()

as appropriate - most probably ReadInt32 as in VB.NET the default for Integer datatype is Int32.

Then the binReader pointer should be properly aligned for starting to read the dData.  The datatype of that is Double, so

                XV.dData(n) = binReader.ReadDouble

should be the right call for that.

You will, however, even if that is OK, have trouble with the VB.NET code when you get to reading sText.  This is because you are only reading - and therefore advancing binReader's pointer by - the number of Doubles that is in iNumPoints and not by the full length of the stored Array(50) of Doubles.  In your VB6 code you calculated the precise position at which the sText data would begin, but in your VB.NET code you are assuming it will start immediately after iNumPoints * Double.  But it won't (except in the case where iNumPoints = 51).  So you will need to revise your loop so that it reads 51 times (to advance the binReader pointer enough), but only stores the data in your structure until iNumPoints is reached.

I reckon that with modifications on the above lines, your VB.NET function should work with a BinaryReader.  But an alternative approach you might like to think about is reading the whole of your file into a byte array and then extracting the individual structures from that using a similar offset calculation approach as you do in your VB6 code.  Or, you could more or less "port" your VB6 code to VB.NET by using the FileGet method instead of Get #.

Incidentallly, your VB.NET Function doesn't return anything, but I assume that is because it is for test purposes, rather than for real.


DO you get an error message?
Does the binReader.Position  change after the readByte..?
Does the ReadByte work instead of the ReadSingle at the first problem.?
What you have read is the first 4 BITS of the data ...

should be

What you have read is the first BYTE of the data ...

Mike TomlinsonMiddle School Assistant TeacherCommented:
Roger brings up a good point about the size of Ints being different in VB.Net.

Important questions:

    Are you reading the OLD file created by VB6?

    ...or are you attempting to read a NEW file that was created by VB.Net code with new .Net types (and consequently new different sizes)?

Have you considered using XML or possibly Binary Serialization instead of Structures?
riceman0Author Commented:

To answer the questions:

* no error messages, just wrong values
* correct, the data in the file is not ordered like the order of the structure -- maybe a little misleading; it is # points, data, text
* the VB.NET class is reading the exact same file as the VB6.0 class, they were both produced by the same ancient DOS program that this is post-processing  :)

I will try the suggestions in the next couple of hours, thanks!
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.