Visual Basic File API Routines
FindFirstFile: Enumerate Folders to a TreeView, Advanced
     
Posted:   Friday July 25, 1997
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch
     

 

Related:  

Enumerating Folders using FindFirstFile and FindNextFile API
FindFirstFile: Fast Directory File Count
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
CopyFileEx: Create a File Backup App
CopyFileEx: Create a File Backup App with a Progress Callback
     
 Prerequisites
None, but this demo is clearer if the related page FindFirstFile: Enumerate Folders to a TreeView has been completed.

vbnsfindfilebasic.gif (5007 bytes)Original recursion routine by Trond Solberg, soltro@Mandatum.no

This code demonstrates how to both recursively and singly search for all folders on a target drive using the Win95/Win98/NT4 FindFirstFile and FindNextFile APIs, and a routine to search just the selected folder. The results are displayed in a treeview control, complete with the appropriate icons for the type of drive installed, and folder icons for the folders. The search depth (single level or all folders), as well as options for sorting the folders and expanding on load are user-selectable.

For a discussion on the FindFirstFile, FindNextFile, and the WIN32_FIND_DATA structure, see Enumerating Folders using FindFirstFile & FindNextFile.

Start a new project, and to the form add a command buttons (Command1), a Treeview control (Treeview1), an auto sizing label (Label1), two checkbox controls (Check1 and Check2), and a control array of 2 option buttons (Option1(0) & Option1(1)), as indicated in the illustration.

Add an imagelist to the form (ImageList1), and populate it with several 16x16 pixel icons. If you would prefer, download a blank form containing the imagelist icons used in this demo rather than construct them yourself.

 BAS Module Code
Place the following code into the general declarations area of a bas module:

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.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const MAX_PATH As Long = 260
         
'GetDriveType return values  
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

Public Declare Function GetDriveType Lib "kernel32" _
   Alias "GetDriveTypeA" _
   (ByVal nDrive As String) As Long

Public Declare Function GetLogicalDriveStrings Lib "kernel32" _
    Alias "GetLogicalDriveStringsA" _
   (ByVal nBufferLength As Long, _
    ByVal lpBuffer As String) 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 FindClose Lib "kernel32" _
  (ByVal hFindFile As Long) As Long
 
'flags for the user options  
Public displayExpanded As Boolean  'integer for VB432
Public displaySorted As Boolean    'integer for VB432
Public LoadAll As Boolean          'integer for VB432
Public NoOfDrives As Integer


Public Function TrimNull(startstr As String) As String

  Dim pos As Integer

  pos = InStr(startstr, Chr$(0))
  
  If pos Then      
      TrimNull = Left$(startstr, pos - 1)
      Exit Function    
  End If
  
 'if this far, there was 
 'no Chr$(0), so return the string 
  TrimNull = startstr
  
End Function


Public Sub GetAllDrivesFolders(tvwTree As Control, nodParentNode As Node)

'this routine uses a pre-dimmed array to speed up 
'processing.  Initially, the array is DIM'med to 
'200 elements; in the While loop it is increased 
'another 200 elements when "found Mod 200 = 0" 
'(or the number found divided by 200 equals 0).  
'At the end of the loop, it is resized down to the 
'total found.  This is significantly faster than 
'using a ReDim Preserve statement for each element found.

  Dim nodX As Node
  
  Dim WFD As WIN32_FIND_DATA
  Dim hFile As Long
  
  Dim sFile As String
  Dim sPath As String
   
  Dim i As Integer
  Dim r As Long
  Dim found As Integer
  
'assign the fullpath property to the path to search,
'assuring that the path is qualified. 
  If Right$(nodParentNode.FullPath, 1) <>  "\" Then
     sPath = nodParentNode.FullPath & "\"
  Else
     sPath = nodParentNode.FullPath
  End If
    
  'strip off the "My Computer" from the FullPath property.
  'The actual fullpath is "My Computer\C:\", however,
  'the Findxxx APIs want only the qualified path, i.e "C:\".      
   sPath = Mid$(sPath, 13, Len(sPath))

 'find the first file matching the parameter \*.* 
  hFile = FindFirstFile(sPath & "*.*" & Chr$(0), WFD)
   
 'reset the counter flag 
  found = 0
  ReDim fArray(200)
  
  If hFile <> -1 Then
    sFile = TrimNull(WFD.cFileName)
    
    WFD.dwFileAttributes = vbDirectory
    
    While FindNextFile(hFile, WFD)
    
      sFile = TrimNull(WFD.cFileName)
        
     'ignore the 2 standard root entries 
      If (sFile <>  ".") And (sFile <>  "..") Then
          
          If (WFD.dwFileAttributes And vbDirectory) Then
          
            found = found + 1
              
           'if found is at 200, then add some more array elements 
            If found Mod 200 = 0 Then ReDim Preserve fArray(found + 200)
            
            fArray(found) = sFile
           
          End If
      
      End If
      
    Wend
    
  End If
  
  Call FindClose(hFile)
      
 'trim down the array to equal the elements found 
  ReDim Preserve fArray(found)
   
 'add the folders to the treeview 
  For i = 1 To found
    
    Set nodX = tvwTree.Nodes.Add(nodParentNode.Key, _
                                 tvwChild, _
                                 sPath & fArray(i) & "Dir", _
                                 fArray(i), 8, 9)
           
   'and get some more 
    If LoadAll Then GetAllDrivesFolders tvwTree, nodX
    
  Next i
  
  nodParentNode.Sorted = displaySorted
  nodParentNode.Expanded = displayExpanded

End Sub


Sub GetNextLevelFolders(tvwTree As Control, nodParentNode As Node)
  
  Dim nodX As Node
  
  Dim WFD As WIN32_FIND_DATA
  Dim hFile As Long
  
  Dim sFile As String
  Dim sPath As String
   
  Dim i As Integer
  Dim r As Long
  Dim found As Integer

 'assign the fullpath property to the path to search,
 'assuring that the path is qualified.    
  If Right$(nodParentNode.FullPath, 1) <>  "\" Then
     sPath = nodParentNode.FullPath & "\"
  Else
     sPath = nodParentNode.FullPath
  End If

 'strip off the "My Computer" from the FullPath property.
 'The actual fullpath is "My Computer\C:\", however,
 'the Findxxx APIs want only the qualified path, i.e "C:\".    
  sPath = Mid$(sPath, 13, Len(sPath))

 'find the first file matching the parameter \*.* 
  hFile = FindFirstFile(sPath & "*.*" & Chr$(0), WFD)
    
  If hFile <>  -1 Then
  
    sFile = TrimNull(WFD.cFileName)
    
    While FindNextFile(hFile, WFD)
    
      sFile = TrimNull(WFD.cFileName)
       
     'ignore the 2 standard root entries    
      If (sFile <>  ".") And (sFile <>  "..") Then
         
          If (WFD.dwFileAttributes And vbDirectory) Then
             'add the item and its icon    
              Set nodX = tvwTree.Nodes.Add(nodParentNode.Key, _
                                           tvwChild, _
                                           sPath & sFile & "Dir", _
                                           sFile, 8, 9)
          End If
      
      End If
      
    Wend
    
  End If
  
  Call FindClose(hFile)
  
  nodParentNode.Sorted = displaySorted
  nodParentNode.Expanded = displayExpanded
   
End Sub
 Form Code
To the form, add two option buttons in a control array, two check boxes, a treeview, and a command button. All controls retain their default names.  Add the following code:

Option Explicit

Private Sub Form_Load()

  'centre the form    
   Me.Move (Screen.Width - Me.Width) / 2, (Screen.Height - Me.Height) / 2

  'set initial options    
   Check2.Value = 1
   Check1.Value = 1
   Option1(0).Value = True

  'load the system drives   
   GetSystemDrives

  'store the initial number of treeview elements for
  'later subtraction when presenting the total number
  'of files loaded (Treeview1_click routine)   
   NoOfDrives = Treeview1.Nodes.Count  
   Label1.Caption = "Set your Load options, then click any drive."

End Sub


Private Sub Command1_Click()

  Unload Me
  
End Sub


Private Sub Check1_Click()

  displayExpanded = Check1.Value = 1

End Sub


Private Sub Check2_Click()

  displaySorted = Check2.Value = 1

End Sub


Private Sub Option1_Click(Index As Integer)

  LoadAll = Option1(1).Value = True

End Sub


Private Sub GetSystemDrives()

  Dim nodX As Node
  Dim r As Long
  Dim allDrives As String
  Dim currDrive As String
  Dim drvIcon As Integer

 'assign the imagelist to the treeview    
  Treeview1.ImageList = ImageList1

 'add a base "My Computer" to the tree   
  Set nodX = Treeview1.Nodes.Add(, , "Root", "My Computer", 1, 1)
  nodX.Expanded = True

 'get the list of all available drives    
  allDrives = rgbGetAvailableDrives()
  
  Do Until allDrives = Chr$(0)
   
    'strip off one drive item from the allDrives$    
     currDrive = StripNulls(allDrives)
     
    'determine the appropriate imagelist icon to display   
     drvIcon = GetDriveDisplayIcon(currDrive)
     
    'we can't have the trailing slash, so ..    
     currDrive = Left$(currDrive, 2)
     
    'Add the drive to the treeview.    
     Set nodX = Treeview1.Nodes.Add("Root", _
                                     tvwChild, _
                                     currDrive$ & "Dir", _
                                     currDrive$, drvIcon, drvIcon)
     nodX.Expanded = True
    
  Loop
  
  'force sorting of the drive letters    
   nodX.Sorted = True

End Sub


Private Function rgbGetAvailableDrives() As String

 'returns a single string of available drive
 'letters, each separated by a chr$(0)   

  Dim r As Long
  Dim tmp As String
  
  tmp = Space$(64)
  Call GetLogicalDriveStrings(Len(tmp), tmp)
  
  rgbGetAvailableDrives = Trim$(tmp)

End Function


Private Function GetDriveDisplayIcon(driveName) As Integer

  Dim dIcon As Integer
  
  Select Case GetDriveType(driveName)
    Case 0, 1:            dIcon = 1
    Case DRIVE_REMOVABLE:
        Select Case Left$(driveName, 1)
          Case "a", "b":  dIcon = 2
          Case Else:      dIcon = 5
        End Select
    
    Case DRIVE_FIXED:     dIcon = 3
    Case DRIVE_REMOTE:    dIcon = 6
    Case DRIVE_CDROM:     dIcon = 4
    Case DRIVE_RAMDISK:   dIcon = 7
  End Select
  
  GetDriveDisplayIcon = dIcon
  
End Function


Private Function StripNulls(startstr As String) As String

 'Take a string separated by chr$(0)
 'and split off 1 item, shortening the
 'string so next item is ready for removal.
  Dim pos As Long

  pos = InStr(startstr$, Chr$(0))
  
  If pos Then
      
      StripNulls = Mid$(startstr, 1, pos - 1)
      startstr = Mid$(startstr, pos + 1, Len(startstr))
    
  End If

End Function


Private Sub Treeview1_Click()

  Dim nodX As Node

  'show a wait message for long searches    
   Label1.Caption = "Searching drive " & _
                     Treeview1.SelectedItem & _
                    " for folders ... please wait"
   DoEvents

  'identify the selected node    
   Set nodX = Treeview1.SelectedItem

  'verify that it is valid
   If (UCase$(Right$(nodX.Key, 3)) = "DIR") And (nodX.Children = 0) Then
     
     'based on the user options, ...     
      If LoadAll Then
            GetAllDrivesFolders Treeview1, nodX
      Else: GetNextLevelFolders Treeview1, nodX
      End If
   
  End If

  'subtract NoOfDrives because "My Computer" and the
  'initial drives loaded should not be counted as a folder    
   Label1.Caption = "Total folders displayed : " & _
                      Treeview1.Nodes.Count - NoOfDrives
  
End Sub
 Comments
Please note that if you select Load All Drive's Folders, and you have a large drive with many folders, the app may appear to hang as it retrieves the information. Therefore, save the project before running, in case you need to Ctrl-Alt-Del out of the routine.

 
 

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