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.
CodePudding user response:
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 Type
s.)
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 5 docs for the attribute here: https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.vbfixedarrayattribute?view=net-5.0
There is also some discussion of the legacy binary file format in the docs for the FileSystem
class in the Microsoft.VisualBasic
namespace.
CodePudding user response:
Arrays of the sort you see in these VB6 Types are not SafeArrays. They're actually vectors, and as such can be replaced with a sequence of the underlying variable: x(3) As Single
can just be x1 As Single
, x2 As Single
, etc in Vb.Net. In contrast, x() As Single
inside the type is a SafeArray and only occupies the 4 bytes used by the descriptor.