Helper class for File and Directory Listing in Win32 API

The following presents a helper class for file and directory listing on Win32 systems using the FindFirstFile and FindNextFile API.

Named CFileEnumerator, it allows enumeration of files from any directory with many customizable options. Constructor of the class accepts a wildcard pattern (such as *.* or ?? etc...) that should be matched for the filename, and a callback function that would be invoked whenever a match is found.

typedef bool (*PFN_EnumeratorCallback)(LPCTSTR lpszItemName, LPCTSTR lpszItemDir, LPWIN32_FIND_DATA lpszItemFindData, LPVOID lpUserData);

CFileEnumerator(PFN_EnumeratorCallback pfnEnumCallback = CFileEnumerator::EnumCallback,	// Callback function for the enumeration
  LPCTSTR lpszSearchDir = _T("."), // Should be a directory path (excluding the file name and wild cards) eg. "c:\\" or "C:\\Dir1"
  LPCTSTR lpszFileToSearch = _T("*.*"),// Should be a file name or wild card eg. "*.*"
  bool bRecurseSubDirectories = true,// Should the sub-directories be recursed?
  bool bReportMatchingDirsAlso = true,// Should the matching directories also be reported while searching for files? - Valid only for file enumeration 
  bool bIgnoreDots = true	// Should . and .. be informed ?? - Valid Only when Listing Directories
  )

As can be observed from the above, CFileEnumerator constructor accepts many parameters to customize the behavior of the enumeration. One can either supply these values as part of the construction of the object, or can rely on the default values. Ofcourse, these values can be modified anytime during the runtime using the accessor methods of the class.

Once a CFileEnumerator object has been constructed with the required options, all that need be done to enumerate the files is - just call EnumerateFiles() method on the constructed object. That will start the search for the file that matches the supplied pattern in the search directory and invokes the supplied enumerator callback function whenever a match is found. The CFileEnumerator::SearchDir() function can be used to access the current search directory and the CFileEnumerator::FileToSearch() function returns the pattern that is being searched for. The CFileEnumerator::RecurseSubDirectories() option specifies whether the search should proceed within the sub-directories of the search directory. Though these options can be set anytime, avoid modifying them while an enumeration is in progress as it could lead to inconsistent behavior.

While EnumerateFiles() supposedly reports only the files in the search directory, the CFileEnumerator::ReportMatchingDirsAlso option, however, can be used to let the enumeration report any matching directories also. For example, if we set CFileEnumerator::ReportMatchingDirsAlso value to be true, and search for a pattern "W*" in the current directory, it will result in the enumerator callback getting invoked not only for the files that matches "W*" pattern but also for any directories that match that pattern.

Ofcourse, one can also use the CFileEnumerator::ListAllDirectories() method to list all the directories (only directories, not files) under the current search directory. However, this method does not take the pattern into accounting while enumerating the directories and hence reports all directories (no matter whether they have the pattern matching or not). This can be useful when we want to get the list all of directories under the search directory (and not interested in the files). When using this function, the CFileEnumerator::IgnoreDots() option can be used to specify whether the . and .. special directories should be reported or not.

Example Usage:
 
#include "FileEnumerator.h"

bool FileEnumeratorCallback(LPCTSTR lpszItemName, LPCTSTR lpszItemDir, LPWIN32_FIND_DATA lpItemFindData, LPVOID lpUserData)
{
    if(lpItemFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        _tprintf(_T("[%s]\n"), CString(lpszItemDir) +lpszItemName); // use [] for directories
    else
        _tprintf(_T("%s\n"), CString(lpszItemDir) + lpszItemName);

    return true; //return true to continue the enumeration. False to stop.
}

int _tmain(int argc, _TCHAR* argv[])
{		
    if(argc !=3)
        exit(_ftprintf(stderr, _T("\nUsage: %s <Search Dir> <Search Pattern>\n\nExample:\n    %s   c:\\   *.cpp\n"), argv[0],argv[0]));

    // Invoke the enumerator class. 
    // The FileEnumeratorCallback function will be called for each file found that matches the search pattern.
    CFileEnumerator _EnumObj(FileEnumeratorCallback, argv[1], argv[2]);

    _EnumObj.RecurseSubDirectories() = false;	// Do not search sub-directories
    _EnumObj.ReportMatchingDirsAlso() = true;	// Report matching directories also while searching for files
    _EnumObj.IgnoreDots() = false;		// Report Dots also while recursing sub-directories
    
    _tprintf(_T("\n\nFile Listing for %s with pattern %s\n\tRecursion:[%s]\n\tReportMatchingDirs:[%s]\n\n"),
            argv[1],argv[2],_EnumObj.RecurseSubDirectories()?_T("ON"):_T("OFF"), _EnumObj.ReportMatchingDirsAlso()?_T("ON"):_T("OFF"));

    if(!_EnumObj.EnumerateFiles())	// List the files that match the search pattern
    {
        _tprintf(_T("\nEnumeration was stopped by user !!\n"));
    }

    _tprintf(_T("\n\nAll directories under %s \n\n"),argv[1]);

    if(!_EnumObj.ListAllDirectories())	// Lists all directories under the search directory (no pattern applied)
    {
        _tprintf(_T("\nEnumeration was stopped by user !!\n"));
    }

    return 0;
} 

Complete source code available at: FileEnumeratorApp.zip

To use this class in your project, you need to copy the FileEnumerator.h and PathHelper.h files from the afore referred source code into your project directory and use #include "FileEnumerator.h" in your implementation file.

By   

Gopalakrishna Palem

Homepage     Other Articles