Visual Basic File API Routines
FindFirstFile: Enumerate Folders to a TreeView
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


Enumerating Folders using FindFirstFile and FindNextFile API, Advanced
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

vbnsfindfilebasic.gif (5007 bytes)Original recursion routine by Trond Solberg,

This code is intended to demonstrate how to recursively search for all folders on a target drive using the Win95/Win98/NT4 FindFirstFile and FindNextFile APIs, and display the results into a treeview control.

FindFirstFile, on successful finding of a file matching the filespec passed, returns a handle for subsequent calls by FindNextFile, and the data of the retrieved file in the WIN32_FIND_DATA structure. The structure member dwFileAttributes, when AND'd with the VB constant vbDirectory can be used to determine if the returned item is a file or folder. The routine stores the folder names in an array, and once a pass is completed at each level, creates the treeview data.

Start a new project, and to the form add a command button (Command1), a Treeview control (Treeview1) and an auto-sizing label (Label1) as indicated in the illustration.

 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 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
Public displaySorted As Boolean
Public NoOfDrives As Integer

Public Function TrimNull(startstr As String) As String

  Dim pos As Long

  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 hFile As Long
   Dim sFile As String
   Dim sPath As String
   Dim i As Long
   Dim r As Long
   Dim found As Long
  'assign the fullpath property to the path to search,
  'assuring that the path is qualified. 
   If Right$(nodParentNode.FullPath, 1) <>  "\" Then
      sPath = nodParentNode.FullPath & "\"
      sPath = nodParentNode.FullPath
   End If
 '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
           'if found is at 200, then add some more array elements 
             found = found + 1           
             If found Mod 200 = 0 Then ReDim Preserve fArray(found + 200)
             fArray(found) = sFile
            End If
         End If
   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", _
    'and get some more 
     GetAllDrivesFolders tvwTree, nodX
   Next i
   nodParentNode.Sorted = displaySorted
   nodParentNode.Expanded = displayExpanded

End Sub
 Form Code
To the form, 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 
  displaySorted = True
  displayExpanded = False
 'load the system drives 
 '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 = "Click on any drive letter to load its folders."

End Sub

Private Sub Command1_Click()

  Unload Me

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
 '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)
   'we can't have the trailing slash, so .. 
    currDrive = Left$(currDrive, 2)
   'add the drive to the treeview 
    Set nodX = Treeview1.Nodes.Add(, tvwChild, _
                                     currDrive & "Dir", _
    nodX.Expanded = True
 '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 tmp As String
  tmp = Space$(64)
  Call GetLogicalDriveStrings(Len(tmp), tmp)
  rgbGetAvailableDrives = Trim$(tmp)

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"
 '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
     GetAllDrivesFolders Treeview1, nodX
  End If
 'subtract NoOfDrives because initial drives loaded 
 'should not be counted as a folder 
  Label1.Caption = "Total folders displayed : " & Treeview1.Nodes.Count - NoOfDrives
End Sub
When the form loads, the treeview is filled with the available drives via the GetSystemDrives routine. Clicking a drive letter invokes the Treeview1_Click() routine, which calls the GetAllDrivesFolders Sub.

The first execution of GetAllDrivesFolders returns the first-level of folders (try commenting out the line "GetAllDrivesFolders tvwTree, nodX" to demonstrate), and for each folder added to the treeview, the routine again calls GetAllDrivesFolders passing the node item to search from.

Once no more folders are found (that is, there are no more folders within a given level), control returns to the calling line, which loads the next folder and restarts the process until all the first-level folders have been searched for sub-folders


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