/
directory.bas
280 lines (206 loc) · 8.06 KB
/
directory.bas
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
Attribute VB_Name = "CodeDIRECTORY"
' (c) Copyright 1995-2024 by John J. Donovan
Option Explicit
Public Const MAX_PATH As Long = 260
Public Const INVALID_HANDLE_VALUE As Long = -1
Public Const FILE_ATTRIBUTE_ARCHIVE As Long = &H20
Public Const FILE_ATTRIBUTE_COMPRESSED As Long = &H800
Public Const FILE_ATTRIBUTE_DIRECTORY = &H10
Public Const FILE_ATTRIBUTE_HIDDEN As Long = &H2
Public Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Public Const FILE_ATTRIBUTE_READONLY As Long = &H1
Public Const FILE_ATTRIBUTE_TEMPORARY As Long = &H100
Public Const FILE_ATTRIBUTE_FLAGS As Long = FILE_ATTRIBUTE_ARCHIVE Or FILE_ATTRIBUTE_HIDDEN Or _
FILE_ATTRIBUTE_NORMAL Or FILE_ATTRIBUTE_READONLY
Public Const DRIVE_UNKNOWNTYPE As Long = 1
Public Const DRIVE_REMOVABLE As Long = 2
Public Const DRIVE_FIXED As Long = 3
Public Const DRIVE_REMOTE As Long = 4
Public Const DRIVE_CDROM As Long = 5
Public Const DRIVE_RAMDISK As Long = 6
Public Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Public Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternate As String * 14
End Type
' Custom-made UDT for searching
Public Type FILE_PARAMS
bRecurse As Boolean ' set True to perform a recursive search
bFound As Boolean ' set only with SearchTreeForFile methods
sFileRoot As String ' search starting point, ie c:\, c:\winnt\
sFileNameExt As String ' filename/filespec to locate, ie *.dll, notepad.exe
sResult As String ' path to file. Set only with SearchTreeForFile methods
nFileCount As Long ' total file count matching filespec. Set in FindXXX only
nFileSize As Double ' total file size matching filespec. Set in FindXXX only
End Type
Public Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
Public Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long
Public Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileA" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Public Declare Function GetLogicalDriveStrings Lib "kernel32" Alias "GetLogicalDriveStringsA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Public Sub DirectorySearch(textension As String, tpath As String, tRecurse As Integer, nCount As Long, sAllFiles() As String)
' Directory search
ierror = False
On Error GoTo DirectorySearchError
Dim FP As FILE_PARAMS
' Load search parameters
With FP
.sFileNameExt = textension$
.sFileRoot = tpath$
.bRecurse = tRecurse%
End With
' Dim an array large enough to hold all the returned values
ReDim sAllFiles(1 To 1000000) As String
' Search for files
nCount& = 0
Screen.MousePointer = vbHourglass
Call DirectorySearchForFilesArray(FP, nCount&, sAllFiles$())
Screen.MousePointer = vbDefault
' Strip off the unused allocated array members
If nCount& > 0 Then ReDim Preserve sAllFiles(1 To nCount&)
Exit Sub
' Errors
DirectorySearchError:
MsgBox Error$, vbOKOnly + vbCritical, "DirectorySearch"
ierror = True
Exit Sub
End Sub
Public Sub DirectorySearchForFilesArray(FP As FILE_PARAMS, nCount As Long, sAllFiles() As String)
' This routine is primarily interested in the directories, so the file type must be *.*
ierror = False
On Error GoTo DirectorySearchForFilesArrayError
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
Dim sPath As String
Dim sRoot As String
Dim sTmp As String
sRoot$ = DirectoryQualifyPath$(FP.sFileRoot)
sPath$ = sRoot & "*.*"
' Obtain handle to the first match
hFile& = FindFirstFile(sPath$, WFD)
' If valid ...
If hFile& <> INVALID_HANDLE_VALUE Then
' DirectoryGetFileInformation function returns the number of files
' matching the filespec (FP.sFileNameExt) in the passed folder
Call DirectoryGetFileInformation(FP, nCount&, sAllFiles$())
Do
' If the returned item is a folder...
If FP.bRecurse And (WFD.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) Then
' Remove trailing nulls
sTmp = DirectoryTrimNull$(WFD.cFileName$)
' And if the folder is not the default self and parent folders...
If sTmp$ <> "." And sTmp$ <> ".." Then
' Get the file
FP.sFileRoot$ = sRoot$ & sTmp$
' This next If..Then just prevents adding extra lines and unneeded paths
' to the array when a file search is performed for a specific file type.
If FP.sFileNameExt$ = "*.*" Then
' Depending on the purpose, you may want to exclude the next 4 optional lines.
' The first two lines adds a blank entry to the array as a separator between new
' directories in the output file. The last two add the directory name alone, before
' listing the files underneath. These four lines can be optionally commented out).
' Obviously, these extra entries will skew the actual file counts.
'nCount& = nCount& + 1
'sAllFiles$(nCount&) = ""
'nCount& = nCount& + 1
'sAllFiles$(nCount&) = FP.sFileRoot$
End If
' Call again
Call DirectorySearchForFilesArray(FP, nCount&, sAllFiles$())
End If
End If
' Continue looping until FindNextFile returns 0 (no more matches)
Loop While FindNextFile(hFile&, WFD)
' Close the find handle
hFile& = FindClose(hFile&)
End If
Exit Sub
' Errors
DirectorySearchForFilesArrayError:
MsgBox Error$, vbOKOnly + vbCritical, "DirectorySearchForFilesArray"
ierror = True
Exit Sub
End Sub
Private Function DirectoryQualifyPath(sPath As String) As String
' Assures that a passed path ends in a slash
ierror = False
On Error GoTo DirectoryQualifyPathError
If Right$(sPath, 1) <> "\" Then
DirectoryQualifyPath$ = sPath$ & "\"
Else
DirectoryQualifyPath$ = sPath$
End If
Exit Function
' Errors
DirectoryQualifyPathError:
MsgBox Error$, vbOKOnly + vbCritical, "DirectoryQualifyPath"
ierror = True
Exit Function
End Function
Private Sub DirectoryGetFileInformation(FP As FILE_PARAMS, nCount As Long, sAllFiles() As String)
' Gets file info for a folder
ierror = False
On Error GoTo DirectoryGetFileInformationError
' Local working variables
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
Dim sPath As String
Dim sRoot As String
Dim sTmp As String
' FP.sFileRoot (assigned to sRoot) contains the path to search
sRoot$ = DirectoryQualifyPath(FP.sFileRoot$)
' FP.sFileNameExt (assigned to sPath) contains the full path and filespec
sPath$ = sRoot$ & FP.sFileNameExt$
' Obtain handle to the first filespec match
hFile& = FindFirstFile(sPath$, WFD)
' If valid ...
If hFile& <> INVALID_HANDLE_VALUE Then
Do
' Even though this routine may use a filespec, *.* is still valid and will cause the search
' to return folders as well as files, so a check against folders is still required.
If Not (WFD.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY Then
' Remove trailing nulls
sTmp$ = DirectoryTrimNull(WFD.cFileName$)
' Increment count and add to the array
nCount& = nCount& + 1
sAllFiles$(nCount&) = sRoot$ & sTmp$
End If
Loop While FindNextFile(hFile&, WFD)
' Close the handle
hFile& = FindClose(hFile&)
End If
Exit Sub
' Errors
DirectoryGetFileInformationError:
MsgBox Error$, vbOKOnly + vbCritical, "DirectoryGetFileInformation"
ierror = True
Exit Sub
End Sub
Private Function DirectoryTrimNull(startstr As String) As String
' Returns the string up to the first null, if present, or the passed string
ierror = False
On Error GoTo DirectoryTrimNullError
Dim pos As Integer
pos = InStr(startstr, vbNullChar)
If pos Then
DirectoryTrimNull = Left$(startstr, pos - 1)
Exit Function
End If
DirectoryTrimNull = startstr
Exit Function
' Errors
DirectoryTrimNullError:
MsgBox Error$, vbOKOnly + vbCritical, "DirectoryTrimNull"
ierror = True
Exit Function
End Function