Visual Basic File API Routines

FindFirstFile: Calculating Folder Size, Recursive
     
Posted:   Saturday March 30, 2002
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP Pro
OS restrictions:   None
Author:   VBnet - Randy Birch
     

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.

Windows does not expose an API that will return the total size of a folder, or the total size of all specified file types (ie *.doc), so we revert to the trusty FindFirstFile and FindNextFile APIs to retrieve this info for us. This demo shows how to retrieve the directory size beginning at the root path, and optionally recursing and including the size of subdirectories under that path. By changing the hard-coded *.* you can restrict the returned value to a specific file type.

As used in the other search demos, a customized User-Defined Type is used as the mechanism to pass multiple search parameters to the search routines. This allows for extending the capabilities of a routine by simply adding a new member to the UDT and coding for it.

The default mode is to search only within 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, without recursion the files in the root are returned, otherwise the recursion searches all folders on the drive.

Note:

  • The demo uses a modified TrimNull method that relies on StrPtr available natively in VB5 and VB6. VB4-32 users will have to declare this as an API exposed by VB4-32 runtime dll (ie in VB5, the declare is Declare Function VarPtr Lib "msvbvm50.dll" (var as Any) As Long).
 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
   nFileCount        As Long
   nFileSize         As Currency '64 bit value
   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 lstrlen Lib "kernel32" _
    Alias "lstrlenW" (ByVal lpString As Long) As Long

Private Declare Function GetTickCount Lib "kernel32" () As Long


Private Sub Form_Load()

   Text1.Text = "c:\windows"
   Check1.Caption = "Recurse"
   Command1.Caption = "GetDirectorySize"
   
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
   Dim fp As FILE_PARAMS
   
   Text2.Text = ""
   Text3.Text = ""
   Text4.Text = ""
   
   With fp
      .sFileRoot = QualifyPath(Text1.Text) 'start path
      .sFileNameExt = "*.*"                'files of interest
      .bRecurse = Check1.Value = 1         'True = recurse
   End With
  
   tstart = GetTickCount()
   Call GetDirectorySize(fp.sFileRoot, fp)
   tend = GetTickCount()
   
   Text2.Text = Format$(fp.nSearched, "###,###,###,##0")
   Text3.Text = Format$(fp.nFileSize, "###,###,###,##0")
   Text4.Text = FormatNumber((tend - tstart) / 1000, 2) & "  seconds"
                                    
End Sub


Private Sub GetDirectorySize(sRoot As String, fp As FILE_PARAMS)

   Dim wfd As WIN32_FIND_DATA
   Dim hFile As Long
  
   hFile = FindFirstFile(sRoot & "*.*", wfd)
  
   If hFile <> INVALID_HANDLE_VALUE Then
   
      Do
                  
         If Asc(wfd.cFileName) <> vbDot Then
            If (wfd.dwFileAttributes And vbDirectory) Then
            
               If fp.bRecurse Then
                  GetDirectorySize sRoot & TrimNull(wfd.cFileName) & "\", fp
               End If  'If fp.bRecurse
            
            Else
         
               fp.nFileCount = fp.nFileCount + 1
               fp.nFileSize = fp.nFileSize + _
                            ((wfd.nFileSizeHigh * _
                             (MAXDWORD + 1)) + _
                              wfd.nFileSizeLow)
         
            End If 'If WFD.dwFileAttributes
            
         End If  'If Asc(wfd.cFileName)
      
         fp.nSearched = fp.nSearched + 1
      
      Loop While FindNextFile(hFile, wfd)
   
   End If 'If hFile
  
   Call FindClose(hFile)

End Sub


Private Function TrimNull(startstr As String) As String

   TrimNull = Left$(startstr, lstrlen(StrPtr(startstr)))
   
End Function


Private Function QualifyPath(sPath As String) As String

   If Right$(sPath, 1) <> "\" Then
      QualifyPath = sPath & "\"
   Else
      QualifyPath = sPath
   End If
      
End Function
 Comments
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