|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||
| Visual Basic File API Routines FindFirstFile: Fast Directory File Count |
||
| Posted: | Thursday September 9, 1999 | |
| Updated: | Monday December 26, 2011 | |
| Applies to: | VB4-32, VB5, VB6 | |
| Developed with: | VB6, Windows NT4 | |
| OS restrictions: | None | |
| Author: | VBnet - Randy Birch | |
|
Related:
|
FindFirstFile: Changing File and/or Folder Attributes Recursively
FindFirstFile: Determining the Oldest Folder (Recursive) 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 |
|
| Prerequisites |
| None. |
|
|
The
routines here demonstrates how to use FindFirstFile, FindNextFile and GetFileAttributes to selectively count all files matching a
given criteria (filespec) and/or file attribute. This makes the routine ideal for counting all files in a given folder (*.*), or for
determining hidden or system files, or directories on a given path.Although the majority of the code is repetitive amongst routines, it is presented this way to isolate each routine for simplicity. In addition, the code (as written) is not recursive, so will only determine the number of files under the initial starting directory. Other VBnet pages show the modifications required to use recursive functions to obtain the total files under the start path. |
| BAS Module Code |
| None. |
|
|
| Form Code |
|
|
| Add three command buttons (Command1, Command2, Command3), and a listbox (List1) to the form, along with 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 INVALID_HANDLE_VALUE As Long = -1
Private Const MAX_PATH As Long = 260
Private Const FILE_ATTRIBUTE_READONLY As Long = &H1
Private Const FILE_ATTRIBUTE_HIDDEN As Long = &H2
Private Const FILE_ATTRIBUTE_SYSTEM As Long = &H4
Private Const FILE_ATTRIBUTE_DIRECTORY As Long = &H10
Private Const FILE_ATTRIBUTE_ARCHIVE As Long = &H20
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Private Const FILE_ATTRIBUTE_TEMPORARY As Long = &H100
Private Const FILE_ATTRIBUTE_COMPRESSED As Long = &H800
Private Const FILE_ATTRIBUTE_ALL As Long = FILE_ATTRIBUTE_READONLY Or _
FILE_ATTRIBUTE_HIDDEN Or _
FILE_ATTRIBUTE_SYSTEM Or _
FILE_ATTRIBUTE_ARCHIVE Or _
FILE_ATTRIBUTE_NORMAL
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 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 GetFileAttributes Lib "kernel32" _
Alias "GetFileAttributesA" _
(ByVal lpFileName As String) As Long
Private Declare Function lstrlenW Lib "kernel32" _
(ByVal lpString As Long) As Long
Private Sub Command1_Click()
Dim sSource As String
Dim sFileType As String
Dim numFiles As Long
'initialize values
sSource = "c:\windows\system32\"
sFileType = "*.*"
'get the count
numFiles = FilesCountAll(sSource, sFileType)
MsgBox numFiles & " files found matching " & sSource & sFileType, _
vbOKOnly Or vbInformation, _
"VBnet FindFirstFile File Count Demo"
End Sub
Private Sub Command2_Click()
Dim sSource As String
Dim sFileType As String
Dim dwAttributes As Long
Dim numFiles As Long
'initialize values
sSource = "c:\windows\"
sFileType = "*.*"
'set the attribute(s) to search for
dwAttributes = FILE_ATTRIBUTE_NORMAL Or FILE_ATTRIBUTE_ARCHIVE
'get the count
numFiles = FilesCountByAttribute(sSource, sFileType, dwAttributes)
MsgBox numFiles & " files found matching " & _
sSource & sFileType & " with attribute(s) " & _
GetAttributeString(dwAttributes), _
vbOKOnly Or vbInformation, _
"VBnet FindFirstFile File Count Demo"
End Sub
Private Sub Command3_Click()
Dim sSource As String
Dim sFileType As String
Dim dwAttributes As Long
Dim numFiles As Long
'initialize values
sSource = "c:\windows\"
sFileType = "*.*"
'set the attribute(s) to search for
dwAttributes = FILE_ATTRIBUTE_ARCHIVE
'clear the list and hide to
'prevent updating until the
'search is complete
With List1
.Clear
.Refresh
.Visible = False
End With
'get the list and count
numFiles = FilesListByAttribute(sSource, sFileType, dwAttributes)
With List1
.Visible = True
.ListIndex = 37
End With
MsgBox numFiles & " files found matching " & _
sSource & sFileType & " with attribute(s) " & _
GetAttributeString(dwAttributes), _
vbOKOnly Or vbInformation, _
"VBnet FindFirstFile File Count Demo"
End Sub
Private Function FilesCountAll(sSource As String, sFileType As String) As Long
Dim wfd As WIN32_FIND_DATA
Dim hFile As Long
Dim fCount As Long
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sSource & sFileType, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
'must have at least one, so ...
Do
'increment the counter and
'find the next file - see comments below
If (Asc(wfd.cFileName) <> vbDot) Then FilesCountAll = FilesCountAll + 1
Loop Until FindNextFile(hFile, wfd) = 0
End If
'Close the search handle
Call FindClose(hFile)
End Function
Private Function FilesCountByAttribute(sSource As String, _
sFileType As String, _
dwAttributes As Long) As Long
Dim wfd As WIN32_FIND_DATA
Dim hFile As Long
hFile = FindFirstFile(sSource & sFileType, wfd)
If (hFile <> INVALID_HANDLE_VALUE) Then
Do
'compare the file attributes
'against the passed flag(s).
'No need to remove trailing nulls.
If (dwAttributes And GetFileAttributes(sSource & wfd.cFileName)) And _
(Asc(wfd.cFileName) <> vbDot) Then
'increment the counter and
'find the next file matching - see comments below
'the file spec
FilesCountByAttribute = FilesCountByAttribute + 1
End If
Loop Until FindNextFile(hFile, wfd) = 0
End If
'Close the search handle
Call FindClose(hFile)
End Function
Private Function FilesListByAttribute(sSource As String, _
sFileType As String, _
dwAttributes As Long) As Long
Dim wfd As WIN32_FIND_DATA
Dim hFile As Long
'Start searching for files
'in the Source directory
hFile = FindFirstFile(sSource & sFileType, wfd)
If (hFile <> INVALID_HANDLE_VALUE) Then
Do
'compare the file attributes
'against the passes flag(s)
If (dwAttributes And GetFileAttributes(sSource & wfd.cFileName)) = dwAttributes And
(Asc(wfd.cFileName) <> vbDot) Then
'increment the counter and find
'the next file matching the file spec
'- see comments below
List1.AddItem TrimNull(wfd.cFileName)
FilesListByAttribute = FilesListByAttribute + 1
End If
Loop Until FindNextFile(hFile, wfd) = 0
End If
'Close the search handle
Call FindClose(hFile)
End Function
Private Function GetAttributeString(attr As Long) As String
Dim tmp As String
If attr And FILE_ATTRIBUTE_ARCHIVE Then tmp = tmp & "ARCHIVE "
If attr And FILE_ATTRIBUTE_NORMAL Then tmp = tmp & "NORMAL "
If attr And FILE_ATTRIBUTE_HIDDEN Then tmp = tmp & "HIDDEN "
If attr And FILE_ATTRIBUTE_READONLY Then tmp = tmp & "READONLY "
If attr And FILE_ATTRIBUTE_SYSTEM Then tmp = tmp & "SYSTEM "
If attr And FILE_ATTRIBUTE_TEMPORARY Then tmp = tmp & "TEMPORARY "
If attr And FILE_ATTRIBUTE_COMPRESSED Then tmp = tmp & "COMPRESSED "
If attr And FILE_ATTRIBUTE_DIRECTORY Then tmp = tmp & "DIRECTORY "
GetAttributeString = tmp
End Function
Private Function TrimNull(startstr As String) As String
TrimNull = Left$(startstr, lstrlenW(StrPtr(startstr)))
End Function
|
| Comments |
| Save the project. Before running, set the desired paths
and file specs in the command click subs. Each routine uses the code "(Asc(wfd.cFileName) <> vbDot)" to prevent counting the two special and hidden directories ('.' and '..') present in every folder. The Asc() code compares the ASCII value of the first letter of the retrieved file against the ASCII value of a period (ASCII 46), skipping the item if a match. The accuracy of this method is predicated on the presumption that users don't *begin* their file names or folders with a period. If you believe this presumption is too, well, presumptuous, there are other options available to eliminate these two system files from the file count, each with corresponding caveats. 1) probably the simplest method is to subtract 2 from the file count if the enumeration was performed on a folder other than the root drive, or subtract 1 if a drive was specified. All subfolders have the '.' (current folder) and '..' (parent folder) members; root folders (drive letters) only have the '.' (current folder) item. 2) Both '.' and '..' have the FILE_ATTRIBUTE_DIRECTORY attribute bit set, thus the If-Then could perform a directory test. And since the routine is to count *files* this won't impact the immediate functionality. It will however prevent the ability to use the same code to perform a directory check if the FILE_ATTRIBUTE_DIRECTORY flag is passed: If (wfd.dwFileAttributes And vbDirectory) <> FILE_ATTRIBUTE_DIRECTORY Then ... 3) Test the items as strings - the least efficient method but the most specific: If (Left$(wfd.cFileName, 1) <> ".") And (Left$(wfd.cFileName, 2) <> "..") Then ... 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). |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |