Prerequisites |
None. |
|

OK .. so the page title is starting to become a tad
unwieldy. But it does accurately represent what this page shows - how to perform
a recursive search for files across a single drive, while specifying a
single or multiple file mask, and whether the search routine should include or exclude the masked files. When
exclude is selected with the demo's *.doc;*.xls;*.ppt mask, the routine returns
all files that are not word docs, excel spreadsheets, or PowerPoint
presentations.
And adding this
functionality is simple. As the
FindFirstFile: Recursive File Search for Single or Multiple File Types (minimal code)
demo showed, PathMatchSpec API returns 1 if the passed filename matched a passed
file spec, or 0 if it didn't. By adding a new Type member to the
FILE_PARAMS type (bFindOrExclude), we can have the MatchSpec function check its
return value from PathMatchSpec against the value we want to know about. If we
selected "Only find listed types", we set bFindOrExclude to 1; if "Find all but
listed types" is selected, we set bFindOrExclude = 0. In MatchSpec, the
function returns True if the return value of the API call is the value we want,
or false otherwise, thus listing the files that match the file specs, or the
files that do not.
Notes:
-
The demo uses the VB6 FormatNumber function
which is not available in VB4-32 or VB5. Users of versions prior to VB6 can
use the API methods shown without any problems, but should change
all FormatNumber references to Format$ in the code below.
-
Any interface to display results (such as the list box shown) will impact
the performance of the routine. If you only need to locate files across a drive
- no need to list the files as in this demo - the routine here is amazingly fast.
Any action that requires constant (re)allocation of string resources
will impede application performance, possibly seriously, as the string size grows.
And I recommend avoiding
*.* searches when adding the files to a list or other interface component.
-
This code
was developed and run on Windows XP Pro. Under Win9x, where the number
of entries in a list box is restricted, there is simply no way to list
over 32k items.
|
|
BAS
Module Code |
None. |
|
|
Form
Code |
|
Create a new project with a form containing
five text boxes (Text1, Text2, Text3, Text4, Text5), a check box (Check1), a
list box (List1), two option buttons in an option array (Option1(0),
Option1(1), and a command button
(Command1). Label as desired and add the
following code. The bolded text indicates the differences between
this code and that from
FindFirstFile: Recursive File Search for Single or Multiple File Types (minimal 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 MAX_PATH As Long = 260
Private Const INVALID_HANDLE_VALUE = -1
Private Const vbBackslash = "\"
Private Const ALL_FILES = "*.*"
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
bFindOrExclude As Long '1=find matching, 0=exclude matching
nCount As Long
nSearched As Long
sFileNameExt As String
sFileRoot As String
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 Declare Function lstrlen Lib "kernel32" _
Alias "lstrlenW" (ByVal lpString As Long) As Long
Private Declare Function PathMatchSpec Lib "shlwapi" _
Alias "PathMatchSpecW" _
(ByVal pszFileParam As Long, _
ByVal pszSpec As Long) As Long
Private fp As FILE_PARAMS 'holds search parameters
Private Sub Form_Load()
Text1.Text = "c:\"
Text2.Text = "*.doc; *.xls; *.ppt"
Option1(0).Value = True
Command1.Caption = "Begin Search"
End Sub
Private Sub Command1_Click()
Dim tstart As Single 'timer var for this routine only
Dim tend As Single 'timer var for this routine only
Text3.Text = ""
Text4.Text = ""
Text5.Text = ""
List1.Clear
List1.Visible = False
With fp
.sFileRoot = QualifyPath(Text1.Text) 'start path
.sFileNameExt = Text2.Text 'file type(s) of interest
.bRecurse = Check1.Value = 1 'True = recursive search
.nCount = 0 'results
.nSearched = 0 'results
.bFindOrExclude = IIf(Option1(0).Value = True, 1, 0) '0=include, 1=exclude
End With
tstart = GetTickCount()
Call SearchForFiles(fp.sFileRoot)
tend = GetTickCount()
List1.Visible = True
Text3.Text = Format$(fp.nSearched, "###,###,###,##0")
Text4.Text = Format$(fp.nCount, "###,###,###,##0")
Text5.Text = FormatNumber((tend - tstart) / 1000, 2) & " seconds"
End Sub
Private Sub SearchForFiles(sRoot As String)
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
hFile = FindFirstFile(sRoot & ALL_FILES, WFD)
If hFile <> INVALID_HANDLE_VALUE Then
Do
'if a folder, and recurse specified, call
'method again
If (WFD.dwFileAttributes And vbDirectory) Then
If Asc(WFD.cFileName) <> vbDot Then
If fp.bRecurse Then
SearchForFiles sRoot & TrimNull(WFD.cFileName) & vbBackslash
End If
End If
Else
'must be a file..
If MatchSpec(WFD.cFileName, fp.sFileNameExt) Then
fp.nCount = fp.nCount + 1
List1.AddItem sRoot & TrimNull(WFD.cFileName)
End If 'If MatchSpec
End If 'If WFD.dwFileAttributes
fp.nSearched = fp.nSearched + 1
Loop While FindNextFile(hFile, WFD)
End If 'If hFile
Call FindClose(hFile)
End Sub
Private Function QualifyPath(sPath As String) As String
If Right$(sPath, 1) <> vbBackslash Then
QualifyPath = sPath & vbBackslash
Else
QualifyPath = sPath
End If
End Function
Private Function TrimNull(startstr As String) As String
TrimNull = Left$(startstr, lstrlen(StrPtr(startstr)))
End Function
Private Function MatchSpec(sFile As String, sSpec As String) As Boolean
MatchSpec = PathMatchSpec(StrPtr(sFile), StrPtr(sSpec)) = fp.bFindOrExclude
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 the method is capable storing (if exceeding the size of a Long), by declaring the appropriate variables as Currency instead
of Long.
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). |
|