Visual Basic File API Routines

FindFirstFile: Recursive File Search Including/Excluding Single or Multiple File Types (minimal code)
     
Posted:   Sunday November 11, 2001
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP Pro
OS restrictions:   None
Author:   VBnet - Randy Birch, Romke Soldaat
     

Related:  

FindFirstFile: Recursive File Search for Single or Multiple File Types (minimal code)
FindFirstFile: Recursive File Search Including/Excluding Single or Multiple File Types (minimal code)
FindFirstFile: Recursive Search for Folders Using a Folder Mask (minimal code)
FindFirstFile: Recursive File Search (minimal code)
FindFirstFile: Recursive Search for Folders (minimal code)
FindFirstFile: Changing File and/or Folder Attributes Recursively
FindFirstFile: Fast Directory File Count
FindFirstFile: Extract Filename from a Full Path
FindFirstFile: Performance Comparison - FSO vs. API
FindFirstFile: Comparison of FindFirstFile and SearchTreeForFile
FindFirstFile: Save a Recursive Search of All Drives to Disk
FindFirstFile: Save a Recursive Search of Specified Drives to Disk
GetFileVersionInfo: File Search and File Property Info
GetLogicalDriveStrings: An API 'DriveExists' Routine
FindFirstFile: An API 'FileExists' Routine
FindFirstFile: An API 'FolderExists' Routine
     
 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).


 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter