Download Visual Basic code for this article here: visual-basic-findfirstfile-win32-api.zip
This article is intended for advanced Visual Basic programmers. It may be interesting for programmers studying Visual Basic programming language as well.
You will need a basic knowledge of the Visual Basic programming language and Win32 API. You need Microsoft Visual Basic 6.0 development environment installed at your computer.
Both functions search a directory for a file whose name matches the specified pattern, but input parameters, results and using are quite different.
The Dir Visual Basic function input parameters:
The Dir function returns a String value representing the name of file or directory that matches file name and/or file attribute patterns.
If you decide to list all files in the some directory and specify the file name pattern, then you need to call the Dir function without parameters until it will return empty string.
The FindFirstFile Win32 API function input parameters:
The FindFirstFile function returns a Long value representing a search handle, if the function succeeds, or INVALID_HANDLE_VALUE, if the function fails.
You need to call the FindNextFile Win32 API function with returned search handle subsequently, if you want to list all files in the directory. You must close the search handle by using the FindClose function when it is no longer needed.
One more distinctive feature: according to my tests, FindFirstFile/FindNextFile Win32 API functions work twice as faster then the Dir Visual Basic function.
Lets review output of the FindNextFile Win32 API function more closely. It populates the WIN32_FIND_DATA structure if some file was found.
Here is the definition of this structure:
typedef struct _WIN32_FIND_DATA
{
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[MAX_PATH];
TCHAR cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
We obtain the following extended file info:
As you can see, we can get all this information by one function call only. We don't need to call GetAttr, FileLen or FileDateTime Visual Basic functions separately. It's a quite big saving of time.
We need the following declarations in our Visual Basic code:
The FindFirstFile Win32 API function, which creates a search handle and obtains first file info
Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" _ (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long
The FindNextFile Win32 API function, which continues a file search from a previous call to the FindFirstFile function
Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileA" _ (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
The FindClose Win32 API function, which closes the search handle created by the FindFirstFile function
Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
The WIN32_FIND_DATA structure that we need to receive the FindFirstFile and the FindNextFile functions output
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 cAlternateFileName As String * 14 End Type
The INVALID_HANDLE_VALUE is return value, which indicates that the FindFirstFile function failed
Const INVALID_HANDLE_VALUE As Long = -1
File name is limited to MAX_PATH characters
Const MAX_PATH As Integer = 260
The FILETIME structure is a 64-bit value representing UTC-based date and time of file
Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
You may notice two disadvantages of the WIN32_FIND_DATA structure from the point of view of Visual Basic programming:
Here is source code for the TrimNull function:
Function TrimNull(sFileName As String) As String Dim i As Long ' Search for the first null character i = InStr(1, sFileName, vbNullChar) If i = 0 Then TrimNull = sFileName Else ' Return the file name TrimNull = Left$(sFileName, i - 1) End If End Function
I think this function is quite simple and doesn't require additional comments.
Now lets implement the FileTimeToDate conversion function. We need two additional Win32 API functions to accomplish this task.
The FILETIME structure contains UTC-based file date and time. Therefore, we need to convert it to the local time according to the current settings for the time zone and daylight saving time.
The FileTimeToLocalFileTime function converts a file time to a local file time
Declare Function FileTimeToLocalFileTime Lib "kernel32" _ (lpFileTime As FILETIME, lpLocalFileTime As FILETIME) As Long
The FileTimeToSystemTime function converts a file time to system time format. It populates the SYSTEMTIME structure, which can be easily used to create Visual Basic Date value.
Declare Function FileTimeToSystemTime Lib "kernel32" _ (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) As Long
Here is the SYSTEMTIME structure, which represents date and time
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
The FileTimeToDate function gets UTC-based file date and time packed in the FILETIME structure and converts it to the Visual Basic Date value.
Function FileTimeToDate(lpFileTime As FILETIME) As Date Dim lpLocalFileTime As FILETIME Dim lpSystemTime As SYSTEMTIME Dim dResult As Date dResult = Empty ' Convert from UTC-based to the local file time If FileTimeToLocalFileTime(lpFileTime, lpLocalFileTime) Then ' Unpack FILETIME structure to SYSTEMTIME structure If FileTimeToSystemTime(lpLocalFileTime, lpSystemTime) Then ' Create Visual Basic Date value dResult = DateSerial(lpSystemTime.wYear, _ lpSystemTime.wMonth, lpSystemTime.wDay) _ + TimeSerial(lpSystemTime.wHour, _ lpSystemTime.wMinute, lpSystemTime.wSecond) End If End If FileTimeToDate = dResult End Function
We have used DateSerial and TimeSerial standard Visual Basic functions to create resulting Date value.
Finally lets review code sample. This code populates txtResult textbox with the list of all files from the location specified in the txtPath textbox. Please note that correct pattern should be specified in the txtPath textbox, i.e. "C:*.*", but not "C:".
' Buffer for output result Dim sBuff As String ' File search handle Dim iSearchHandle As Long ' File search buffer Dim pFindFileBuff As WIN32_FIND_DATA sBuff = vbNullString ' Find first file and create search handle iSearchHandle = FindFirstFile(Me.txtPath.Text, pFindFileBuff) ' Check if FindFirstFile call was successful If iSearchHandle <> INVALID_HANDLE_VALUE Then ' Store first file name and date in the buffer sBuff = TrimNull(pFindFileBuff.cFileName) & " " _ & CStr(FileTimeToDate(pFindFileBuff.ftLastWriteTime)) & vbCrLf ' Find the rest of files with the FindNextFile function Do While FindNextFile(iSearchHandle, pFindFileBuff) sBuff = sBuff & TrimNull(pFindFileBuff.cFileName) & " " _ & CStr(FileTimeToDate(pFindFileBuff.ftLastWriteTime)) & vbCrLf Loop ' Close file search handle Call FindClose(iSearchHandle) End If ' Show results Me.txtResult.Text = sBuff
As you can see we have utilized all information reviewed above in this small Visual Basic sample. Feel free to download and run full source code.
Here are some recommendations on how to use sample Visual Basic code: