vb.netvb6migrationrandomaccessfile

Upgrading VB6 method to read Random-access file to VB.net, Unable to read beyond the end of the stream Exception for last record


I am recreating an older VB6 program that reads random-access files created using another VB6 program in VB.net. Backwards compatibility is essential for the new VB.net program. There are thousands of files that have been written that need to be accessed. There are five lines to each of the files when I open them in notepad++, though I can't make heads or tails of the random characters in notepad++. The files should contain four records, so I am not sure what the fifth line is for.

I have access to the old source code for both programs. Below are the VB6 read and write methods.

VB6 Write Method

Dim fi as long
fi = FreeFile
Open fileName For Random As #fi Len = 32000

Put #fi, 1, Loads
Put #fi, 2, Nodes
Put #fi, 3, Names
Put #fi, 4, Options

Close fi

VB6 Read Method

Dim fi As Long      
fi = FreeFile
Open fileName For Random As #fi Len = 32000

Get #fi, 1, Loads
Get #fi, 2, Nodes
Get #fi, 3, Names
Get #fi, 4, Options

Close fi

VB,net Read Method (attempt)
In VB.net, I have the following that I am using to try to read the same files.

Dim fi As Integer = FreeFile()
FileOpen(fi, fileName, OpenMode.Random, RecordLength:=32000)

FileGet(fi, Loads, 1)
FileGet(fi, Nodes, 2)
FileGet(fi, Names, 3)
FileGet(fi, Options, 4)

FileClose(fi)

Loads, Nodes, Names, and Options are [user-defined] types in VB6, and structures in VB.net. These structures often have other structures inside of them, and those structures contain more structures. Some of the structures in the hierarchy contain arrays of other structures, which may have arrays of yet more structures inside of them. Most structure fields were singles or booleans. There were a few longs in the VB6 that I switched to be integers in VB.net (to keep the byte size the same). There are a few strings fields as well.

When I try to use the VB.net read method above, it gets values for Loads, Nodes, and Names. On the line FileGet(fi, Options, 4), I get: Systems.IO.EndOfStreamException: 'Unable to read beyond the end of the stream.'

Looking at the two methods to read above, shouldn't they work the same? What exactly is causing this exception in vb.net? Is the record size not <= 32,000 bytes?


Edit

Options is based off typOptions, which uses typDesign and typGeometry. VB6:

Public Const numDebug = 12
Public Type typOptions
    optDesign As typDesign
    optDebug(numDebug) As Boolean
    optGeom As typGeometry
    optFrac As Single
End Type
Public Type typDesign
    mem01 As Single
    mem02 As Single
    mem03 As Single
    mem04 (3) As Single
End Type
Public Type typGeometry
    member1 As Single
    member2 As Single
    member3 As Single
    member4 As Single
    member5 As Single
    member6 As Single
    member7 As Single
End Type

VB.net

Public Const numDebug = 12
Public Structure typOptions
    Public optDesign As typDesign
    Public optDebug() As Boolean
    Public optGeom As typGeometry
    Public optFrac As Single
End Structure
Public Structure typDesign
    Public mem01 As Single
    Public mem02 As Single
    Public mem03 As Single
    Public mem04 () As Single
End Structure
Public Structure typGeometry
    Public member1 As Single
    Public member2 As Single
    Public member3 As Single
    Public member4 As Single
    Public member5 As Single
    Public member6 As Single
    Public member7 As Single
End Structure
' Elsewhere, when the form is loading:
ReDim Options.optDesign.mem04 (3)
ReDim Options.optDebug(numDebug)

EDIT 2

The file size for the files seems to be consistently 96,124 bytes. Only 124 bytes are being stored for Options instead of 32,000. I tried using a hex editor and adding "00" until there was 128,000 bytes (32,000 bytes per record * 4 records), but then I still get the Unable to read beyond the end of the stream exception. The 128,000 byte file did work in VB6 though.

I found some information about this from vbmigration.com:

VB6 and VB.NET greatly differ in how UDTs - Structures in VB.NET parlance - are read from or written to files. Not only are structure elements stored to file in a different format, but the two languages also manage the End-of-File condition in a different way. In VB6 you can read a UDT even if the operation would move the file pointer beyond the current file’s length; in VB.NET such an operation would cause an exception.

Based on that, I would have thought that making the file 128,000 bytes instead of 96,124 would have fixed the unable to read beyond the end of stream exception.


Solution

  • I believe the issue is that you have your structure declared with dynamic arrays, but the VB6 structure is declared with fixed-length arrays (which the other answer at the time of this writing also notes). The legacy binary file processing expects a variable-length array in your structure to correspond to an array descriptor in the binary file, so it's perhaps not surprising that trying to read a binary floating point value as an array descriptor leads to problems.

    I think the best approach for this is to use the VBFixedArray attribute on your array members. The legacy binary file processing understands this attribute, and it should then read the file correctly. (There is also a VBFixedString attribute for describing fixed-length strings from VBA Types.)

    The result would be something like this:

    Public Structure typDesign
        Public mem01 As Single
        Public mem02 As Single
        Public mem03 As Single
        <VBFixedArray(3)>
        Public mem04() As Single
    End Structure
    

    You can find the .NET 6 docs for the attribute here: https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.vbfixedarrayattribute?view=net-6.0

    There is also some discussion of the legacy binary file format in the docs for the FileSystem class in the Microsoft.VisualBasic namespace.