Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / WinMobile

Directory file listing utility

3.28/5 (21 votes)
13 Jun 2012CPOL3 min read 103K   3.8K  
An article about methods to recursively list all files in a given directory.

Image 1

Introduction

I've come across countless situations where routinely used utilitarian functions are simply not available, and coders simply have to write and rewrite them many times over. This is my first article in a series of articles which will try to provide a collection of routinely used functions with example applications on how to use them, in the hope that they will be as useful to the many developers out there as it has been to me.

One such function is to obtain a list of all files (their absolute paths) given a directory to start with, with options to search recursively into sub-directories or just to output the files present directly under the given directory.

I've found this to be extremely useful in many projects which required going through each file for some reason or the other.

Background

This function was developed many years ago when I stumbled upon a barrage of projects that required reading into every file in a given directory, and also provide progress information in real time, e.g., which file is being currently processed, total number of files, and number of files already processed. Since then, I've probably used this function in a few dozen projects.

Using the Code

Follow these simple steps to use the code in your project:

  1. Add the files Util.h and Util.cpp into your Visual Studio C++ project (or any other kind of project, I have never used the code on anything other than Visual Studio C++ projects, so any input on using it on other C++ projects is appreciated).
  2. Add the line #include "Util.h" in the top section of the *.cpp files you intend to use this function in.
  3. Declare a vector of _tstring's: vector <_tstring> vecstrFileList;.
  4. Call the static function "GetFileList()" with the required parameters.
  5. The vector will now have all the file paths found in the directory specified, each element of the vector being a string representing the absolute path of a file. Note that you must explicitly clear the vector before reusing it, unless you want the function to append to the vector. The function will also work on network drive directory paths.
  6. Take a look at the "Get file list" button event handler function in the source code to see steps 1 to 5 in action.
  7. Check the "Util.cpp" source file, or the code snippet below, for a detailed description of how the function is working internally, it is extensively commented.
C++
void CUtil::GetFileList(const _tstring& strTargetDirectoryPath, 
     const _tstring& strWildCard, bool bLookInSubdirectories, 
     vector<_tstring>& vecstrFileList)
{
    // Check whether target directory string is empty
    if(strTargetDirectoryPath.compare(_T("")) == 0)
    {
        return;
    }

    // Remove "\\" if present at the end of the target directory
    // Then make a copy of it and use as the current search directory
    _tstring strCurrentDirectory = RemoveDirectoryEnding(strTargetDirectoryPath);

    // This data structure stores information about the file/folder
    // that is found by any of these Win32 API functions:
    // FindFirstFile, FindFirstFileEx, or FindNextFile function
    WIN32_FIND_DATA fdDesktop = {0};

    // Format and copy the current directory
    // Note the addition of the wildcard *.*, which represents all files
    // 
    // Below is a list of wildcards that you can use
    // * (asterisk)      - represents zero or more characters
    //                     at the current character position
    // ? (question mark) - represents a single character
    //
    // Modify this function so that the function can take in a search
    // pattern with wildcards and use it in the line
    // below to find for e.g. only *.mpg files
    //
    // "\\?\" prefix to the file path means that the
    // file system supports large paths/filenames
    _tstring strDesktopPath = _T("");
    strDesktopPath += _T("\\\\?\\");
    strDesktopPath += strCurrentDirectory;
    strDesktopPath = AddDirectoryEnding(strDesktopPath);

    if(strWildCard.compare(_T("")) == 0)
    {
        strDesktopPath += _T("*.*");
    }
    else
    {
        strDesktopPath += strWildCard;
    }

    // Finds the first file and populates the
    // WIN32_FIND_DATA data structure with its information
    // The return value is a search handle used in subsequent
    // calls to FindNextFile or FindClose functions
    HANDLE hDesktop = ::FindFirstFile(strDesktopPath.c_str(), &fdDesktop);    

    // If an invalid handle is returned by FindFirstFile function,
    // then the directory is empty, so nothing to do but quit
    if(hDesktop == INVALID_HANDLE_VALUE)
    {
        return;
    }

    // Do this on the first file found and repeat for every next
    // file found until all the required files
    // that match the search pattern are found
    do 
    {
        // Reconstruct the path
        _tstring strPath = _T("");
        strPath += strCurrentDirectory;
        strPath = AddDirectoryEnding(strPath);
        strPath += fdDesktop.cFileName;

        // Check if a directory was found
        if(fdDesktop.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            // Get the name of the directory
            _tstring strCurrentDirectoryName = GetDirectoryName(strPath);

            // If its a current (.) or previous (..)
            // directory indicator, just skip it
            if((strCurrentDirectoryName.compare(_T(".")) == 0) || 
               (strCurrentDirectoryName.compare(_T("..")) == 0))
            {
                continue;
            }
            // Other wise this is a sub-directory
            else
            {
                // Check whether function was called
                // to include sub-directories in the search
                if(bLookInSubdirectories)
                {
                    // If sub-directories are to be searched as well,
                    // recursively call the function again,
                    // with the target directory as the sub-directory
                    GetFileList(strPath, strWildCard, bLookInSubdirectories, 
                                vecstrFileList);
                }
            }
        }
        // A file was found
        else
        // if(fdDesktop.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
        {
            // Add the string to the vector
            vecstrFileList.push_back(_tstring(strPath));
        }
    }
    // Search for the next file that matches the search pattern
    while(::FindNextFile(hDesktop, &fdDesktop) == TRUE);

    // Close the search handle
    ::FindClose(hDesktop);
}

Points of Interest

The code provided here is for unmanaged C++; if you are using managed code or C#, which uses the .NET Framework, you can use the Directory.GetFiles methods; you can read about it here.

While writing this code, I found that, to my annoyance, Microsoft Windows does not have any easy to use unmanaged C++ code to actually do this in a straightforward and simple manner. If you do find this code useful, please leave a comment, it could make my day Smile | :)

Known Issues

When recursively finding files in sub-directories using a wild card, if the parent directory does not have at least one such file, the nested directories will not be searched.

History

  • V1.0 - Initial release.
  • V1.1 - Fixed recursive sub-directory search.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)