|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Prerequisites |
| None. |
|
|
VB's
Random Access file routines provides a powerful way to create well
defined file structures and to access that data without the restrictions
applied to normal sequential
file operations. Random Access files are particularly well suited for
applications that do not require the weight and complexity of a database
back end, such as a personal contact manager or other similar
application.
One of the difficulties encountered in using random access files is the deletion of outdated records. While the conventional and recommended programming practice is to include a flag member in the UDT that defines the file record that can be flipped to indicate that - on the next save - the particular record should be overwritten with the data of a new record. And while this is certainly a recommended practice (as compared with the only other option of writing the existing data to a new file up top the record to be deleted, skipping that record, and continuing to the end of the file), having the ability to delete records from the end of a random access file is not outside the abilities of a few API calls. Random access files - along with the methods to manipulate random access file data - is unique to VB; Windows knows nothing about random access file access, and as such the Windows APIs for writing to files can't manipulate the file's data in the same way that VB can. But Windows does provide a specific API - SetEndOfFile - that can be used against a random access file to truncate one or more records. The demo as coded below does three things:
The structure and layout of the UDT for this demo was made to provide you with an easy-to-see confirmation of the start and end data for each record when printed to the immediate window. Once you've reached the second step, the records will display data similar to:
begin record 1
the
end of 14 When the first truncation takes place, the file will be cut immediately before "begin record 15", and so on for each subsequent press. Given the code and comments, it would be a small task to alter the routine to accept a value that indicated the number of records to delete from the end of the file other than just one, since the number of records to delete times the size of one record is all that would need to be subtracted from the file total size to affect his modification. |
| BAS Module Code |
| None. |
|
|
| Form Code |
|
|
| Add three command buttons (Command1 / Command2 / Command3) to a form along with the following code: |
|
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'change this to a suitable location on your system!
Private Const sTestFile As String = "d:\test.txt"
Private Const OPEN_ALWAYS As Long = 4
Private Const FILE_BEGIN As Long = 0
Private Const GENERIC_WRITE As Long = &H40000000
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80
'test file UDT
Private Type TestData
s1 As String * 15
s2 As String * 5
dw1 As Long
dt1 As Date
dw2 As Long
s3 As String * 13
End Type
Private Declare Function CreateFile Lib "kernel32" _
Alias "CreateFileA" _
(ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" _
(ByVal hfile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" _
(ByVal hfile As Long, _
ByVal lDistanceToMove As Long, _
lpDistanceToMoveHigh As Long, _
ByVal dwMoveMethod As Long) As Long
Private Declare Function SetEndOfFile Lib "kernel32" _
(ByVal hfile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hfile As Long) As Long
Private Sub Form_Load()
Command1.Caption = "Create random file"
Command2.Caption = "Read random file data"
Command3.Caption = "Delete last random record"
End Sub
Private Sub Command1_Click()
Dim td As TestData
Dim nodata As TestData
Dim cnt As Long
Dim hfile As Long
hfile = FreeFile
Open sTestFile For Random Access Write As #hfile Len = Len(td)
'add 15 random records to the test file
For cnt = 1 To 15
With td
'for debug purposes, this line is
'the first line of each record written
.s1 = "begin record " & cnt
'for debug purposes, this line is
'the last line of each record written
RSet .s3 = "the end of " & cnt
'some other random data for the record
.s2 = String(Len(.s1), cnt + 96)
.dw1 = 1234
.dw2 = 3445
.dt1 = Now
End With
Put #hfile, cnt, td
td = nodata
Next cnt
Close #hfile
Debug.Print "file created"
End Sub
Private Sub Command2_Click()
Dim td As TestData
Dim nodata As TestData
Dim cnt As Long
Dim hfile As Long
Dim totalrecords As Long
hfile = FreeFile
Open sTestFile For Random Access Read As #hfile Len = Len(td)
totalrecords = LOF(hfile) \ Len(td)
For cnt = 1 To totalrecords
Get #hfile, cnt, td
With td
Debug.Print .s1
Debug.Print .s2
Debug.Print .dw1
Debug.Print .dw2
Debug.Print .dt1
Debug.Print .s3
End With
td = nodata
Next cnt
Close #hfile
End Sub
Private Sub Command3_Click()
DeleteLastRandomRecord
End Sub
Private Sub DeleteLastRandomRecord()
Dim td As TestData
Dim hfile As Long
Dim totalrecords As Long
Dim newfilesize As Long
Dim dwFileSizeLow As Long
Dim dwFileSizeHigh As Long
'how many records in the file?
totalrecords = FileLen(sTestFile) \ Len(td)
'can't truncate the file if
'totalrecords is less than 2
If totalrecords >= 2 Then
'open the file using the API
hfile = CreateFile(sTestFile, _
GENERIC_WRITE Or GENERIC_READ, _
0&, ByVal 0&, _
OPEN_ALWAYS, _
FILE_ATTRIBUTE_NORMAL, _
0&)
'Get the file size. Note that the value
'returned is the size of the file as
'stored in the low word of GetFileSize with
'the high word returning 0. This will
'accommodate most reasonably-sized files.
'If the file is large, however, then the low
'and high word values have to be be combined
'to get the actual size. This demo assumes a
'you're using a reasonably sized random access
'file, so a test of dwFileSizeHigh is made
'to ensure it is 0 before truncating.
dwFileSizeLow = GetFileSize(hfile, dwFileSizeHigh)
If dwFileSizeHigh = 0 Then
'dwFileSizeLow for this test is the
'file size; one record is Len(td),
'so in order to delete the last
'record the new size is the current
'size minus the length of one record:
newfilesize = dwFileSizeLow - Len(td)
Debug.Print totalrecords, dwFileSizeLow, newfilesize
'now move the file pointer to the
'new end position ..
If SetFilePointer(hfile, newfilesize, dwFileSizeHigh, FILE_BEGIN) > 0 Then
'and "delete" the last record by
'changing the end of file marker
Call SetEndOfFile(hfile)
End If 'SetFilePointer
End If 'dwFileSizeHigh
CloseHandle hfile
End If
End Sub
|
| Comments |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |