-
Notifications
You must be signed in to change notification settings - Fork 0
/
HugeBinaryFile.cls
310 lines (272 loc) · 8.85 KB
/
HugeBinaryFile.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "HugeBinaryFile"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
'
'HugeBinaryFile
'==============
'
'A class for doing simple binary I/O on very large disk files
'(well over the usual 2GB limit). It only does I/O using Byte
'arrays, and makes use of Currency values that are scaled to
'whole numbers in places:
'
' For a file of one byte the FileLen property returns 1.0000 as
' its value.
'
'Operation is similar in many ways to native VB Get#/Put# I/O, for
'example the EOF property must be checked after a ReadBytes() call.
'You must also Dim/Redim buffers to desired sizes before calling
'ReadBytes() or WriteBytes().
'
'Short (signed Long) relative seeks and long (unsigned Currency)
'absolute seeks from 0 may be done.
'
'AutoFlush may be set True to force buffer flushes on every write.
'The Flush() method may be called explicitly if necessary.
'
Public Enum HBF_Errors
HBF_UNKNOWN_ERROR = 45600
HBF_FILE_ALREADY_OPEN
HBF_OPEN_FAILURE
HBF_SEEK_FAILURE
HBF_FILELEN_FAILURE
HBF_READ_FAILURE
HBF_WRITE_FAILURE
HBF_FILE_ALREADY_CLOSED
End Enum
Private Const HBF_SOURCE = "HugeBinaryFile"
Private Const GENERIC_WRITE As Long = &H40000000
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80&
Private Const CREATE_ALWAYS = 2
Private Const OPEN_ALWAYS = 4
Private Const INVALID_HANDLE_VALUE = -1
Private Const INVALID_SET_FILE_POINTER = -1
Private Const INVALID_FILE_SIZE = -1
Private Const FILE_BEGIN = 0, FILE_CURRENT = 1, FILE_END = 2
Private Type MungeCurr
Value As Currency
End Type
Private Type Munge2Long
LowVal As Long
HighVal As Long
End Type
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageW" ( _
ByVal dwFlags As Long, _
lpSource As Long, _
ByVal dwMessageId As Long, _
ByVal dwLanguageId As Long, _
ByVal lpBuffer As Long, _
ByVal nSize As Long, _
Arguments As Any) As Long
Private Declare Function ReadFile Lib "kernel32" ( _
ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
ByVal lpOverlapped As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" ( _
ByVal hFile As Long, _
lpFileSizeHigh As Long) As Long
Private Declare Function WriteFile Lib "kernel32" ( _
ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, _
ByVal lpOverlapped As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileW" ( _
ByVal lpFileName As Long, _
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 SetFilePointer Lib "kernel32" ( _
ByVal hFile As Long, _
ByVal lDistanceToMove As Long, _
lpDistanceToMoveHigh As Long, _
ByVal dwMoveMethod As Long) As Long
Private Declare Function FlushFileBuffers Lib "kernel32" ( _
ByVal hFile As Long) As Long
Private hFile As Long
Private sFName As String
Private fAutoFlush As Boolean
Private fEOF As Boolean
Private C As MungeCurr
Private L As Munge2Long
Public Property Get AutoFlush() As Boolean
RaiseErrorIfClosed
AutoFlush = fAutoFlush
End Property
Public Property Let AutoFlush(ByVal NewVal As Boolean)
RaiseErrorIfClosed
fAutoFlush = NewVal
End Property
Public Property Get FileHandle() As Long
RaiseErrorIfClosed
FileHandle = hFile
End Property
Public Property Get FileLen() As Currency
RaiseErrorIfClosed
L.LowVal = GetFileSize(hFile, L.HighVal)
If L.LowVal = INVALID_FILE_SIZE Then
If Err.LastDllError Then RaiseError HBF_FILELEN_FAILURE
End If
LSet C = L
FileLen = C.Value * 10000@
End Property
Public Property Get FileName() As String
RaiseErrorIfClosed
FileName = sFName
End Property
Public Property Get EOF() As Boolean
RaiseErrorIfClosed
EOF = fEOF
End Property
Public Property Get IsOpen() As Boolean
IsOpen = hFile <> INVALID_HANDLE_VALUE
End Property
Public Sub CloseFile()
RaiseErrorIfClosed
CloseHandle hFile
sFName = ""
fAutoFlush = False
fEOF = False
hFile = INVALID_HANDLE_VALUE
End Sub
Public Sub Flush()
RaiseErrorIfClosed
FlushFileBuffers hFile
End Sub
Public Sub OpenFile(ByVal OpenFileName As String, Optional ByVal WriteAccess As Boolean = True)
If hFile <> INVALID_HANDLE_VALUE Then
RaiseError HBF_FILE_ALREADY_OPEN
End If
hFile = CreateFile(StrPtr(OpenFileName), IIf(WriteAccess, GENERIC_WRITE, 0) Or GENERIC_READ, 0, _
0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
If hFile = INVALID_HANDLE_VALUE Then
RaiseError HBF_OPEN_FAILURE
End If
sFName = OpenFileName
End Sub
Public Function ReadBytes(ByRef Buffer() As Byte) As Long
RaiseErrorIfClosed
If ReadFile(hFile, _
Buffer(LBound(Buffer)), _
UBound(Buffer) - LBound(Buffer) + 1, _
ReadBytes, _
0) Then
If ReadBytes = 0 Then
fEOF = True
End If
Else
RaiseError HBF_READ_FAILURE
End If
End Function
Public Sub SeekAbsolute(ByVal Position As Currency)
RaiseErrorIfClosed
C.Value = Position / 10000@
LSet L = C
If SetFilePointer(hFile, L.LowVal, L.HighVal, FILE_BEGIN) _
= INVALID_SET_FILE_POINTER Then
If Err.LastDllError Then RaiseError HBF_SEEK_FAILURE
End If
End Sub
Public Sub SeekEnd()
RaiseErrorIfClosed
If SetFilePointer(hFile, 0&, ByVal 0&, FILE_END) _
= INVALID_SET_FILE_POINTER Then
RaiseError HBF_SEEK_FAILURE
End If
End Sub
Public Sub SeekRelative(ByVal Offset As Long)
'Offset is signed.
RaiseErrorIfClosed
If SetFilePointer(hFile, Offset, ByVal 0&, FILE_CURRENT) _
= INVALID_SET_FILE_POINTER Then
RaiseError HBF_SEEK_FAILURE
End If
End Sub
Public Function WriteBytes(Buffer() As Byte) As Long
RaiseErrorIfClosed
If WriteFile(hFile, _
Buffer(LBound(Buffer)), _
UBound(Buffer) - LBound(Buffer) + 1, _
WriteBytes, _
0) Then
If fAutoFlush Then Flush
Else
RaiseError HBF_WRITE_FAILURE
End If
End Function
Private Sub Class_Initialize()
hFile = INVALID_HANDLE_VALUE
End Sub
Private Sub Class_Terminate()
If hFile <> INVALID_HANDLE_VALUE Then CloseHandle hFile
End Sub
Private Sub RaiseError(ByVal ErrorCode As HBF_Errors)
Dim Win32Err As Long, Win32Text As String
Exit Sub ' TODO: Fix this
Win32Err = Err.LastDllError
If Win32Err Then
Win32Text = vbNewLine & "Error " & Win32Err & vbNewLine _
& DecodeAPIErrors(Win32Err)
End If
If IsOpen Then CloseFile
Select Case ErrorCode
Case HBF_FILE_ALREADY_OPEN
Err.Raise HBF_FILE_ALREADY_OPEN, HBF_SOURCE, _
"File already open."
Case HBF_OPEN_FAILURE
Err.Raise HBF_OPEN_FAILURE, HBF_SOURCE, _
"Error opening file." & Win32Text
Case HBF_SEEK_FAILURE
Err.Raise HBF_SEEK_FAILURE, HBF_SOURCE, _
"Seek Error." & Win32Text
Case HBF_FILELEN_FAILURE
Err.Raise HBF_FILELEN_FAILURE, HBF_SOURCE, _
"GetFileSize Error." & Win32Text
Case HBF_READ_FAILURE
Err.Raise HBF_READ_FAILURE, HBF_SOURCE, _
"Read failure." & Win32Text
Case HBF_WRITE_FAILURE
Err.Raise HBF_WRITE_FAILURE, HBF_SOURCE, _
"Write failure." & Win32Text
Case HBF_FILE_ALREADY_CLOSED
Err.Raise HBF_FILE_ALREADY_CLOSED, HBF_SOURCE, _
"File must be open for this operation."
Case Else
Err.Raise HBF_UNKNOWN_ERROR, HBF_SOURCE, _
"Unknown error." & Win32Text
End Select
End Sub
Private Sub RaiseErrorIfClosed()
If hFile = INVALID_HANDLE_VALUE Then RaiseError HBF_FILE_ALREADY_CLOSED
End Sub
Private Function DecodeAPIErrors(ByVal ErrorCode As Long) As String
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000&
Dim strMsg As String, lngMsgLen As Long
strMsg = Space$(256)
lngMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, _
ErrorCode, 0&, StrPtr(strMsg), 256&, 0&)
If lngMsgLen > 0 Then
DecodeAPIErrors = Left(strMsg, lngMsgLen)
Else
DecodeAPIErrors = "Unknown Error."
End If
End Function