Microsoft KB Archive/185476

= How To Search Directories to Find or List Files =

Article ID: 185476

Article Last Modified on 7/13/2004

-

APPLIES TO


 * Microsoft Visual Basic 5.0 Learning Edition
 * Microsoft Visual Basic 6.0 Learning Edition
 * Microsoft Visual Basic 5.0 Professional Edition
 * Microsoft Visual Basic 6.0 Professional Edition
 * Microsoft Visual Basic 5.0 Enterprise Edition
 * Microsoft Visual Basic 6.0 Enterprise Edition
 * Microsoft Visual Basic 4.0 Standard Edition
 * Microsoft Visual Basic 4.0 Professional Edition
 * Microsoft Visual Basic 4.0 32-Bit Enterprise Edition

-



This article was previously published under Q185476



SUMMARY
When looking for files, it is often necessary to search through subdirectories. This article demonstrates two methods for recursively searching directories and retrieving file information.



MORE INFORMATION
While Visual Basic provides methods for retrieving information about files and directories, you may also use Windows API functions for these tasks. Using the API is not faster than the built in methods, but the two methods work a little differently. So, this article demonstrates both techniques for retrieving this information. If you test both methods, try using the same starting path and search string. You should get similar results.

Visual Basic for Applications (VBA) includes an Application.FileSearch object that can be used to find and list files. While not directly available in Visual Basic, you can use it from a Microsoft Office 97 product. More information and examples can be found in the Online Help, and by searching the Microsoft Knowledge Base for "FileSearch."

Please note that the following examples do not include full error trapping, but Method 2 does catch a special case where the VB GetAttr function fails on Pagefile.sys, which is the Windows NT virtual memory paging file. Also, depending on the search string, the API version lists and counts directory names by default, where the VB version does not. Please note that the following examples do not include full error trapping, but Method 2 does catch a special case where the VB GetAttr function fails on Pagefile.sys, which is the Windows NT virtual memory paging file. The only difference in results between these two methods is that the VB code does not return the file create dates.

Method 1: Using the Windows API
 Start a new Standard EXE project in Visual Basic. Form1 is created by default. Add a CommandButton named Command1, four TextBoxes named Text1, Text2, Text3 and Text4 and a ListBox to Form1.  Add a Module from the Projects menu and insert the following: Declare Function FindFirstFile Lib "kernel32" Alias _ "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData _  As WIN32_FIND_DATA) As Long

Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileA" _ (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long

Declare Function GetFileAttributes Lib "kernel32" Alias _ "GetFileAttributesA" (ByVal lpFileName As String) As Long

Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) _ As Long

Declare Function FileTimeToLocalFileTime Lib "kernel32" _ (lpFileTime As FILETIME, lpLocalFileTime As FILETIME) As Long Declare Function FileTimeToSystemTime Lib "kernel32" _ (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) As Long

Public Const MAX_PATH = 260 Public Const MAXDWORD = &HFFFF Public Const INVALID_HANDLE_VALUE = -1 Public Const FILE_ATTRIBUTE_ARCHIVE = &H20 Public Const FILE_ATTRIBUTE_DIRECTORY = &H10 Public Const FILE_ATTRIBUTE_HIDDEN = &H2 Public Const FILE_ATTRIBUTE_NORMAL = &H80 Public Const FILE_ATTRIBUTE_READONLY = &H1 Public Const FILE_ATTRIBUTE_SYSTEM = &H4 Public Const FILE_ATTRIBUTE_TEMPORARY = &H100

Type FILETIME dwLowDateTime As Long dwHighDateTime As Long End Type

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

Type SYSTEMTIME wYear As Integer wMonth As Integer wDayOfWeek As Integer wDay As Integer wHour As Integer wMinute As Integer wSecond As Integer wMilliseconds As Integer End Type

Public Function StripNulls(OriginalStr As String) As String If (InStr(OriginalStr, Chr(0)) > 0) Then OriginalStr = Left(OriginalStr, _         InStr(OriginalStr, Chr(0)) - 1) End If     StripNulls = OriginalStr End Function   Copy the following code into Form1's module: Option Explicit

Function FindFilesAPI(path As String, SearchStr As String, _   FileCount As Integer, DirCount As Integer) Dim FileName As String  ' Walking filename variable... Dim DirName As String   ' SubDirectory Name Dim dirNames As String ' Buffer for directory name entries Dim nDir As Integer  ' Number of directories in this path Dim i As Integer     ' For-loop counter... Dim hSearch As Long  ' Search Handle Dim WFD As WIN32_FIND_DATA Dim Cont As Integer Dim FT As FILETIME Dim ST As SYSTEMTIME Dim DateCStr As String, DateMStr As String If Right(path, 1) <> "\" Then path = path & "\" ' Search for subdirectories. nDir = 0 ReDim dirNames(nDir) Cont = True hSearch = FindFirstFile(path & "*", WFD) If hSearch <> INVALID_HANDLE_VALUE Then Do While Cont DirName = StripNulls(WFD.cFileName) ' Ignore the current and encompassing directories. If (DirName <> ".") And (DirName <> "..") Then ' Check for directory with bitwise comparison. If GetFileAttributes(path & DirName) And _ FILE_ATTRIBUTE_DIRECTORY Then dirNames(nDir) = DirName DirCount = DirCount + 1 nDir = nDir + 1 ReDim Preserve dirNames(nDir) ' Uncomment the next line to list directories 'List1.AddItem path & FileName End If        End If         Cont = FindNextFile(hSearch, WFD)  ' Get next subdirectory. Loop Cont = FindClose(hSearch) End If

' Walk through this directory and sum file sizes. hSearch = FindFirstFile(path & SearchStr, WFD) Cont = True If hSearch <> INVALID_HANDLE_VALUE Then While Cont FileName = StripNulls(WFD.cFileName) If (FileName <> ".") And (FileName <> "..") And _ ((GetFileAttributes(path & FileName) And _ FILE_ATTRIBUTE_DIRECTORY) <> FILE_ATTRIBUTE_DIRECTORY) Then FindFilesAPI = FindFilesAPI + (WFD.nFileSizeHigh * _            MAXDWORD) + WFD.nFileSizeLow FileCount = FileCount + 1 ' To list files w/o dates, uncomment the next line ' and remove or Comment the lines down to End If           'List1.AddItem path & FileName ' Include Creation date... FileTimeToLocalFileTime WFD.ftCreationTime, FT          FileTimeToSystemTime FT, ST           DateCStr = ST.wMonth & "/" & ST.wDay & "/" & ST.wYear & _ " " & ST.wHour & ":" & ST.wMinute & ":" & ST.wSecond ' and Last Modified Date FileTimeToLocalFileTime WFD.ftLastWriteTime, FT          FileTimeToSystemTime FT, ST           DateMStr = ST.wMonth & "/" & ST.wDay & "/" & ST.wYear & _ " " & ST.wHour & ":" & ST.wMinute & ":" & ST.wSecond List1.AddItem path & FileName & vbTab & _ Format(DateCStr, "mm/dd/yyyy hh:nn:ss") _ & vbTab & Format(DateMStr, "mm/dd/yyyy hh:nn:ss") End If        Cont = FindNextFile(hSearch, WFD)  ' Get next file Wend Cont = FindClose(hSearch) End If

' If there are sub-directories... If nDir > 0 Then ' Recursively walk into them... For i = 0 To nDir - 1 FindFilesAPI = FindFilesAPI + FindFilesAPI(path & dirNames(i) _        & "\", SearchStr, FileCount, DirCount) Next i  End If   End Function

Private Sub Command1_Click Dim SearchPath As String, FindStr As String Dim FileSize As Long Dim NumFiles As Integer, NumDirs As Integer

Screen.MousePointer = vbHourglass List1.Clear SearchPath = Text1.Text FindStr = Text2.Text FileSize = FindFilesAPI(SearchPath, FindStr, NumFiles, NumDirs) Text3.Text = NumFiles & " Files found in " & NumDirs + 1 & _ " Directories" Text4.Text = "Size of files found under " & SearchPath & " = " & _ Format(FileSize, "#,###,###,##0") & " Bytes" Screen.MousePointer = vbDefault End Sub  Run the Project. Enter a starting path into Text1, a search string in Text2 (like *.* or *.txt) and then click Command1.

You will see a list of the files found display in the ListBox with the create date and the last modified date, the actual number of files found displays in Text3, and the total size of the files found under the starting directory appears in Text4.

Method 2: Using Built-In Visual Basic Functions
These instructions build on the sample described prior, but can also be used in a new Project.  Open the Project by using the steps described in Method1</li> Add another CommandButton named Command2, two more TextBoxes named Text5 and Text6 and another ListBox, List2, to Form1.</li>  Copy the following code into Form1's module: Function FindFiles(path As String, SearchStr As String, _      FileCount As Integer, DirCount As Integer) Dim FileName As String  ' Walking filename variable. Dim DirName As String   ' SubDirectory Name. Dim dirNames As String ' Buffer for directory name entries. Dim nDir As Integer     ' Number of directories in this path. Dim i As Integer        ' For-loop counter.

On Error GoTo sysFileERR If Right(path, 1) <> "\" Then path = path & "\" ' Search for subdirectories. nDir = 0 ReDim dirNames(nDir) DirName = Dir(path, vbDirectory Or vbHidden Or vbArchive Or vbReadOnly _ Or vbSystem) ' Even if hidden, and so on. Do While Len(DirName) > 0 ' Ignore the current and encompassing directories. If (DirName <> ".") And (DirName <> "..") Then ' Check for directory with bitwise comparison. If GetAttr(path & DirName) And vbDirectory Then dirNames(nDir) = DirName DirCount = DirCount + 1 nDir = nDir + 1 ReDim Preserve dirNames(nDir) 'List2.AddItem path & DirName ' Uncomment to list End If                          ' directories. sysFileERRCont: End If        DirName = Dir  ' Get next subdirectory. Loop

' Search through this directory and sum file sizes. FileName = Dir(path & SearchStr, vbNormal Or vbHidden Or vbSystem _     Or vbReadOnly Or vbArchive) While Len(FileName) <> 0 FindFiles = FindFiles + FileLen(path & FileName) FileCount = FileCount + 1 ' Load List box List2.AddItem path & FileName & vbTab & _ FileDateTime(path & FileName)  ' Include Modified Date FileName = Dir ' Get next file. Wend

' If there are sub-directories.. If nDir > 0 Then ' Recursively walk into them For i = 0 To nDir - 1 FindFiles = FindFiles + FindFiles(path & dirNames(i) & "\", _           SearchStr, FileCount, DirCount) Next i     End If

AbortFunction: Exit Function sysFileERR: If Right(DirName, 4) = ".sys" Then Resume sysFileERRCont ' Known issue with pagefile.sys Else MsgBox "Error: " & Err.Number & " - " & Err.Description,, _ "Unexpected Error" Resume AbortFunction End If     End Function

Private Sub Command2_Click Dim SearchPath As String, FindStr As String Dim FileSize As Long Dim NumFiles As Integer, NumDirs As Integer

Screen.MousePointer = vbHourglass List2.Clear SearchPath = Text1.Text FindStr = Text2.Text FileSize = FindFiles(SearchPath, FindStr, NumFiles, NumDirs) Text5.Text = NumFiles & " Files found in " & NumDirs + 1 & _ " Directories" Text6.Text = "Size of files found under " & SearchPath & " = " & _ Format(FileSize, "#,###,###,##0") & " Bytes" Screen.MousePointer = vbDefault End Sub

Private Sub Form_Load Command1.Caption = "Use API code" Command2.Caption = "Use VB code" ' start with some reasonable defaults Text1.Text = "C:\My Documents\" Text2.Text = "*.*" End Sub </li> Run the Project. Enter a starting path into Text1, a search string in Text2 (like *.* or Myfile?.txt, and so forth) and then click Command2.</li></ol>

You see a list of the files found appear in List2 with the last modified date, the number of files found in Text5, and the total size of the files found under the starting directory in Text6. By combining these two methods on one form you can verify that both methods return matching information.

Method 3: Use the FileSystem Object with Visual Basic
For information about using the FileSystemObject with Visual Basic to find or list files, please see the following article in the Microsoft Knowledge Base:

185601 How To Recursively Search Directories Using FileSystemObject

Keywords: kbhowto kbapi KB185476

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.