VB6随机访问文件读取时偶现记录重复问题求助
VB6随机访问文件读取偶现记录数据重复问题
问题现象
读取老旧VB6随机访问文件时,偶尔会出现一条记录的数据被复制到下一条记录的情况。实现相同功能的VB.Net文件流代码完全正常,且VB6的相关代码近期没做过任何修改。
VB6 读取代码
Property Let Location(ByVal Source As String) FileName = Source + ShortFileName FileNumber = FreeFile Open FileName For Random Access Read Write Shared As FileNumber Len = 200 FileOpen = True End Property Public Sub Read(ByVal RecordNumber As Long) Get FileNumber, RecordNumber, FileData() RecordLocked = False End Sub
VB .Net 读取代码
public string Location { set { FileName = value + ShortFileName; strOriginalFilePath = value; fs = new FileStream(FileName, FileMode.Open, System.IO.FileAccess.ReadWrite, FileShare.ReadWrite, BufferLength, FileOptions.RandomAccess); IsFileOpen = true; } } public void Read(long RecordNumber) { checked { bool flag = default(bool); while (!flag) { try { fs.Position = (RecordNumber - 1) * BufferLength; fs.Read(FileData, 0, BufferLength); flag = true; } catch (Exception projectError) { ProjectData.SetProjectError(projectError); Application.DoEvents(); ProjectData.ClearProjectError(); } } } }
问题原因
- VB6缓存同步bug:VB6的
Open For Random默认依赖系统文件缓存,当文件被其他进程(比如你的VB.Net程序)以共享模式访问时,缓存同步容易出现异常,导致读取到旧的缓存数据,表现为记录重复。 - 不必要的写权限:VB6代码用
Access Read Write打开文件,但实际只做读取操作,多余的写权限会增加文件锁和缓存冲突的概率;VB.Net虽然也用了ReadWrite,但文件流的缓存处理逻辑更稳定。 - 数组残留数据:VB6的
FileData()数组如果没在每次读取前重置,会残留上一次的读取数据,要是某次读取因为锁冲突没成功,旧数据就会被保留,造成重复的假象。
修复方案
1. 改成只读模式打开文件
既然只需要读取,把VB6的打开语句改成只读,减少冲突:
Open FileName For Random Access Read Shared As FileNumber Len = 200
2. 每次读取前重置数组
确保FileData数组没有残留数据,读取后强制同步缓存:
Public Sub Read(ByVal RecordNumber As Long) ' 重置数组长度为200字节(下标0-199) ReDim FileData(199) Get FileNumber, RecordNumber, FileData() ' 调用FileLen触发缓存同步,确保读到最新数据 FileLen(FileName) RecordLocked = False End Sub
3. 增加错误重试逻辑
VB6的Get遇到文件锁不会抛异常,可能返回错误数据,加个重试逻辑:
Public Sub Read(ByVal RecordNumber As Long) Dim retryCount As Integer retryCount = 0 RecordLocked = True ' 最多重试3次 Do While retryCount < 3 On Error Resume Next ReDim FileData(199) Get FileNumber, RecordNumber, FileData() If Err.Number = 0 Then RecordLocked = False Exit Do End If Err.Clear retryCount = retryCount + 1 DoEvents ' 等其他进程释放锁 Loop End Sub
4. 禁用VB6文件缓存(可选)
通过API强制禁用文件缓存,直接读磁盘:
Private Declare Function SetFileAttributes Lib "kernel32" Alias "SetFileAttributesA" (ByVal lpFileName As String, ByVal dwFileAttributes As Long) As Long Private Const FILE_FLAG_NO_BUFFERING = &H20000000 Property Let Location(ByVal Source As String) FileName = Source + ShortFileName ' 设置文件无缓存属性 SetFileAttributes FileName, FILE_FLAG_NO_BUFFERING FileNumber = FreeFile Open FileName For Random Access Read Shared As FileNumber Len = 200 FileOpen = True End Property
内容的提问来源于stack exchange,提问作者RandomTree
相关产品推荐
相关产品推荐

