Introduction
Often we need to enumerate files and/or directories and end up writing code like this (pseudocode):
HANDLE hFind = FindFirstFile(...)
while(!lastfile)
{
FindNextFile(...);
}
And since it is so simple we write it again and again. As simple as it seems, it is prone to many errors and we make these errors as often as we write this simple code.
This was the main reason for me to create a template based class to enumerate files and directories. The class does not care about the actual data you need to preserve, or how you represent it. It just cares about correctly recursing a folder and enumerating its content.
The main features of this template class are:
- thread safety
- separation of logic from data
- simple to use
Implementation
The implementation is as follows:
for each folder (which is not '.' or '..')
if folder should be used
run the loop again in this folder
for each file
if file should be used
handle the filename
handle the file
finish processing
The most vital and used functions are the following virtual functions:
virtual bool CheckUseDir(LPCTSTR pstrPath,
WIN32_FIND_DATA* pwfd);
This function is called for every found folder. If the folder should be used, the function has to return true
, returning false
will skip this folder. The default implementation returns always true.
virtual bool CheckUseFile(LPCTSTR pstrPath,
WIN32_FIND_DATA* pwfd);
This function is called for every found file. It gets passed the full find-file information. If the file should be used, the function has to return true
, returning false
will skip this file. The default implementation returns always true.
You can implement simple filtering by filename (or extension) like this:
bool CheckUseFile(LPCTSTR, WIN32_FIND_DATA* pwfd)
{
return ::PathMatchSpec(pwfd->cFileName, _T("*.jpg"));
}
virtual bool HandleRawFile(LPCTSTR pstrFile);
This function is called for every file found after CheckUseFile
returned true
. If the file should be further used, the function has to return true
, returning false
will skip this file. The default implementation returns always true.
This function is designed to implement a higher level file processing as it is necessary when you process a archive file. If a archive is considered valid during this function call, one might extract it and then loop again over the extracted files. In this case this function would return false.
virtual void HandleFile(T* pFile);
This function is the last to be called to use the file. Normally one would then store the file information in an array or list.
virtual void FinishedDir(LPCTSTR pstrDir);
After a folder is completely processed this function is called. The folder is not more touched after this function call. You can safely delete the folder for example.
Error handling
If errors occur during any stage of the processing, an error handler (HandleError()
) is called. The function is provided with location of the error and the error code. The location is one of the locations defined (RDLOC_xxx
)in the class header. The error code is the error code returned by GetLastError()
.
HandleError()
has to return one of the error continuation codes defined (RDEH_xxx
) in the class header. You can cause the enumeration to continue with the next folder or file, continue normally, abort or fail by returning the appropriate code.
Starting/Stopping
The file and folder enumeration is started by calling Run(...)
with a directory as parameter. You may or may not add a backslash to the directory. The function takes care of proper handling in any case.
To stop the enumeration at any time, call CancelRun()
. Stopping is implemented through an event object. If you must attach the class to an existing event you can call SetEvent(...)
with your own event handle before starting the enumeration.
Samples
The source code contains also two often used variations: class CDirectoryContent
and class CCleanDir
.
CDirectoryContent
delivers an array (std::vector
or CArray
) of files contained in the directory provided and CCleanDir
recursively erases all files in the directory.
The demo project includes a CDirectoryContent
derived class which implements a simple wildcard filter.
Compatibility
Written, compiled and tested with VC6 SP5. Unicode safe. Requires Version 4.71 or later of Shlwapi.dll (uses Path...()
functions). If used with in a MFC project it will use CString
and CArray
classes, otherwise it uses std::string
and std::vector
.
References
Have a look also at some of the other implementations here at CodeProject: