升级 VB6 方法以将随机访问文件读取到 VB.net,无法读取超出流末尾的最后一条记录异常

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

我正在重新创建一个较旧的 VB6 程序,该程序读取使用 VB.net 中的另一个 VB6 程序创建的随机访问文件。 向后兼容性对于新的 VB.net 程序至关重要。 有数千个已写入的文件需要访问。当我在 notepad++ 中打开它们时,每个文件都有五行,尽管我无法在 notepad++ 中制作随机字符的正面或反面。这些文件应该包含四个记录,所以我不确定第五行是什么。

我可以访问这两个程序的旧源代码。下面是VB6种读写方法

VB6写法

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 读取方法

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,网络读取方法(尝试)
在 VB.net 中,我使用以下内容来尝试读取相同的文件。

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)

负载、节点、名称和选项在 VB6 中是 [用户定义的] 类型,在 VB.net 中是结构。这些结构内部通常有其他结构,并且这些结构包含更多结构。层次结构中的某些结构包含其他结构的数组,这些结构内部可能包含更多结构的数组。大多数结构字段是单数或布尔值。 VB6 中有一些长整数,我在 VB.net 中将其转换为整数(以保持字节大小相同)。还有一些字符串字段。

当我尝试使用上面的 VB.net 读取方法时,它获取负载、节点和名称的值。在 FileGet(fi, Options, 4) 行,我得到: Systems.IO.EndOfStreamException: 'Unable to read beyond the end of the stream.'

看上面的两种阅读方式,应该是一样的吧? vb.net 中究竟是什么导致了这个异常?记录大小是否 <= 32,000 字节?


编辑

Options 基于 typOptions,它使用 typDesign 和 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)

编辑 2

文件的文件大小似乎始终为 96,124 字节。只为选项存储了 124 个字节,而不是 32,000 个。我尝试使用十六进制编辑器并添加“00”直到有 128,000 字节(每条记录 32,000 字节 * 4 条记录),但我仍然得到 无法读取超出流结尾的内容 例外。 128,000 字节的文件确实在 VB6 中有效。

我从 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.

基于此,我原以为将文件设为 128,000 字节而不是 96,124 字节可以解决无法读取超出流末尾的异常。

您在这些 VB6 类型中看到的那种数组不是 SafeArray。它们实际上是向量,因此可以用底层变量的序列替换:x(3) As Single 可以是 x1 As Singlex2 As Single 等 Vb.Net。相比之下,类型中的x() As Single一个SafeArray,只占用描述符使用的4个字节。

我认为问题是您的结构是用动态数组声明的,但 VB6 结构是用固定长度数组声明的(撰写本文时的另一个答案也指出了这一点)。遗留二进制文件处理期望结构中的可变长度数组对应于二进制文件中的数组描述符,因此尝试将二进制浮点值读取为数组描述符会导致问题可能并不奇怪。

我认为最好的方法是在数组成员上使用 VBFixedArray 属性。遗留的二进制文件处理理解这个属性,然后它应该正确地读取文件。 (还有一个 VBFixedString 属性用于描述来自 VBA Types 的固定长度字符串。)

结果会是这样的:

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

您可以在此处找到该属性的 .NET 5 文档: https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.vbfixedarrayattribute?view=net-5.0

Microsoft.VisualBasic 命名空间中 FileSystem class 的文档中也有一些关于遗留二进制文件格式的讨论。