Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / ATL
Article

File and Directory Enumeration

Rate me:
Please Sign up or sign in to vote.
4.61/5 (15 votes)
3 Mar 2003Ms-PL4 min read 203.6K   4.6K   62   28
Template based file and directory enumeration class.

Introduction

Often we need to enumerate files and/or directories and end up writing code like this (pseudocode):

HANDLE hFind = FindFirstFile(...)
while(!lastfile)
{
    // do something if the file is not "." and not ".."
    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, <BR>    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, <BR>    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); // T being the typename

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:

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior)
Portugal Portugal
Software Smith, Blacksmith, Repeat Founder, Austrian, Asgardian.

Comments and Discussions

 
QuestionIs this really the best way ? Pin
yarp4-Mar-03 19:02
yarp4-Mar-03 19:02 
AnswerRe: Is this really the best way ? Pin
Andreas Saurwein5-Mar-03 4:22
Andreas Saurwein5-Mar-03 4:22 
I think there is no best way. Depending on what you want to achive you choose either FindFirst/FintNext or the Shell version.
If you need only raw files/directories then the Find... variant is pretty much all that you need. And it is far faster than the Shell variant.
If you require other items from the namespace too, the Shell method mentioned by you is the way to go. But for files and folders it is overkill.




Shaken, stirred, or strained through a diaper, nothing can make a martini palatable. Roger Wright, Soapbox
GeneralRe: Is this really the best way ? Pin
yarp5-Mar-03 7:03
yarp5-Mar-03 7:03 
GeneralRe: Is this really the best way ? Pin
Andreas Saurwein6-Mar-03 5:04
Andreas Saurwein6-Mar-03 5:04 
GeneralRe: Is this really the best way ? Pin
yarp6-Mar-03 7:15
yarp6-Mar-03 7:15 
GeneralRe: Is this really the best way ? Pin
Andreas Saurwein7-Mar-03 0:26
Andreas Saurwein7-Mar-03 0:26 
GeneralRe: Is this really the best way ? Pin
yarp7-Mar-03 6:13
yarp7-Mar-03 6:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.