|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Prerequisites |
| None. |
|
|
VBnet
already contains several pages that show how to use the FindFirstFile and
FindNextFile APIs to rapidly search for files or folders. Yet
several programmers have asked for pages that provide just the most
minimal code to retrieve either file or folder info, across single or
multiple drives. This series of four pages address these requests.Certainly the easiest way to pass multiple search parameters to search routines is via a customized User-Defined Type, and this demo, like the other FindFirstFile demos, uses a UDT to allow for extending the capabilities of a routine by simply adding a new member to the UDT. This page shows the minimalist code for searching a singe drive for all folders under the specified start path. Searching for folders requires just search routine, with a couple of support methods to parse the returned strings once it has been determined that a folder has been located. The routine is extremely fast, as the illustration shows, and for a non-recursive search is just a bit faster than a corresponding Dir() method. However, the routine really shines in a recursive search as it is considerably faster than a recursive Dir() method. Compared to the FindFirstFile methods , the FileSystemObject is, well, no comparison. Create your own demo to see the huge performance hit that the collection in the FSO imparts on your search or see the comparison demo in Related above. The default mode coded is to search only the specified folder. When the "Recurse" button is checked, the specified folder, and all subfolders under it, are searched. When a drive alone is specified as the source path, the recursion searches all folders on the drive. Notes:
|
| BAS Module Code |
| None. |
|
|
| Form Code |
|
|
| Create a new project with a form containing four text boxes (Text1, Text2, Text3, Text4), a check boxes (Check1), a list box (List1) and a command button (Command1). Label as desired and add 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. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Const vbDot = 46
Private Const MAXDWORD As Long = &HFFFFFFFF
Private Const MAX_PATH As Long = 260
Private Const INVALID_HANDLE_VALUE = -1
Private Const FILE_ATTRIBUTE_DIRECTORY = &H10
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private 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
Private Type FILE_PARAMS
bRecurse As Boolean
sFileRoot As String
sFileNameExt As String
sResult As String
sMatches As String
Count As Long
End Type
Private Declare Function FindClose Lib "kernel32" _
(ByVal hFindFile As Long) As Long
Private Declare Function FindFirstFile Lib "kernel32" _
Alias "FindFirstFileA" _
(ByVal lpFileName As String, _
lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" _
Alias "FindNextFileA" _
(ByVal hFindFile As Long, _
lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Sub Command1_Click()
Dim FP As FILE_PARAMS 'holds search parameters
Dim tstart As Single 'timer var for this routine only
Dim tend As Single 'timer var for this routine only
'clear results textbox and list
Text3.Text = ""
'set up search params
With FP
.sFileRoot = Text1.Text 'start path
.sFileNameExt = Text2.Text 'file type of interest
.bRecurse = Check1.Value = 1 '1 = do recursive search
End With
'setting the list visibility to false
'increases clear and load time
List1.Visible = False
List1.Clear
'get start time, folders, and finish time
tstart = GetTickCount()
Call SearchForFolders(FP)
tend = GetTickCount()
List1.Visible = True
'show the results
Text3.Text = Format$(FP.Count, "###,###,###,##0") & _
" found (" & _
FP.sFileNameExt & ")"
Text4.Text = FormatNumber((tend - tstart) / 1000, 2) & " seconds"
End Sub
Private Sub SearchForFolders(FP As FILE_PARAMS)
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
Dim sRoot As String
Dim spath As String
Dim sTmp As String
sRoot = QualifyPath(FP.sFileRoot)
spath = sRoot & FP.sFileNameExt
'obtain handle to the first match
hFile = FindFirstFile(spath, WFD)
'if valid ...
If hFile <> INVALID_HANDLE_VALUE Then
Do
'Only folders are wanted, so discard files
'or parent/root DOS folders.
If (WFD.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) And _
Asc(WFD.cFileName) <> vbDot Then
'must be a folder, so remove trailing nulls
sTmp = TrimNull(WFD.cFileName)
'This is where you add code to store
'or display the returned file listing.
'
'if you want the folder name only, save 'sTmp'.
'if you want the full path, save 'sRoot & sTmp'
FP.Count = FP.Count + 1
List1.AddItem sRoot & sTmp
'if a recursive search was selected, call
'this method again with a modified root
If FP.bRecurse Then
FP.sFileRoot = sRoot & sTmp
Call SearchForFolders(FP)
End If
End If
Loop While FindNextFile(hFile, WFD)
'close the handle
hFile = FindClose(hFile)
End If
End Sub
Private Function TrimNull(startstr As String) As String
'returns the string up to the first
'null, if present, or the passed string
Dim pos As Integer
pos = InStr(startstr, Chr$(0))
If pos Then
TrimNull = Left$(startstr, pos - 1)
Exit Function
End If
TrimNull = startstr
End Function
Private Function QualifyPath(spath As String) As String
'assures that a passed path ends in a slash
If Right$(spath, 1) <> "\" Then
QualifyPath = spath & "\"
Else
QualifyPath = spath
End If
End Function
|
| Comments |
| Before running, assure that any hard-coded paths reflect accurate paths on your system.
It must also be noted that, since this example uses the listbox to return the results, on systems containing many files you may, eventually, hit the listbox item limit of 32k items under an non-NT-based system. While there is no practical or reliable way to extend the number of items a listbox can contains on a Win9x system (without resorting to an owner-drawn control), you can increase the number of files read (if exceeding the size of a Long), by declaring the appropriate variables as Currency instead. Note: While it may be convenient to utilize VB's built-in constants in place of the FILE_ATTRIBUTE_* API values, care must be taken. There is a difference between related constant values that may cause unexpected performance at some point. For example, the constant 'vbNormal' is defined as having a value of 0, whereas the API FILE_ATTRIBUTE_NORMAL has a value of &H80 (decimal 128). |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |