Recurrsivly iterating through directories

Started by
9 comments, last by _moagstar_ 14 years, 8 months ago
Im using the win32 fuctions FindFirstFile and FindNextFile to creating a Tree control that shows all the folders and sub folders in a current directory. But am having real trouble trying to get it to work. Im testing for "." and "..", but FindNextFile keeps returning bad pointers. Im sure this is due to my error. And im just wondering if there is an easier way of doing what i want. That is getting the directory layout, not building the tree control.
Advertisement
If using C++, you could just use boost::filesystem. If not, mentioning what programming language you are using might help.
Here's a somewhat simple but I believe complete example of using the FindFile API for you to compare against.

#include <stdio.h>#include <windows.h>void FindAllFiles(const char * searchThisDir_, bool searchSubDirs, void (*UserFileFunc)(const char *, const char *), void (*UserFolderFunc)(const char *, const char *)){	WIN32_FIND_DATAA FindFileData = {0};	HANDLE hFind = INVALID_HANDLE_VALUE;	char searchDir[2048] = {0};	char searchThisDir[2048] = {0};	size_t searchThisDirLen = 0;	// Convert all /'s to \'s	_snprintf(searchThisDir, 2047, "%s\\", searchThisDir_);	searchThisDirLen = strlen(searchThisDir);	for(size_t x = 0; x < searchThisDirLen; ++x)	{		if(searchThisDir[x] == '/')			searchThisDir[x] = '\\';	}	// We always want the path to end with a directory separator	// (but must fix two escaped \'s in a row)	if(searchThisDirLen >= 2 && searchThisDir[searchThisDirLen - 2] == '\\')	{		searchThisDir[searchThisDirLen - 1] = 0;		searchThisDirLen--;	}	// Begin finding files and folders!	_snprintf(searchDir, 2047, "%s*", searchThisDir);	hFind = FindFirstFileA(searchDir, &FindFileData);	if(hFind == INVALID_HANDLE_VALUE)	{		return;	}	do	{		// Ignore the 'current directory' folder		if(strcmp(FindFileData.cFileName, ".") == 0)		{			continue;		}		// Ignore the 'parent directory' folder		if(strcmp(FindFileData.cFileName, "..") == 0)		{			continue;		}		// If we have a folder		if(FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)		{			if(UserFolderFunc) UserFolderFunc(searchThisDir, FindFileData.cFileName);			if(searchSubDirs)			{				char searchDir2[2048] = {0};				_snprintf(searchDir2, 2047, "%s%s", searchThisDir, FindFileData.cFileName);				FindAllFiles(searchDir2, true, UserFileFunc, UserFolderFunc);			}			continue;		}		// Otherwise we have a file		if(UserFileFunc) UserFileFunc(searchThisDir, FindFileData.cFileName);	}	while(FindNextFileA(hFind, &FindFileData) != 0);	FindClose(hFind);}void UserFileFunc(const char * path, const char * title){	printf("Path: %s\nTitle: %s\n\n", path, title);}void UserFolderFunc(const char * path, const char * title){	printf("Path: %s\nTitle: %s\n\n", path, title);}int main(int argc, char* argv[]){	char curPath[MAX_PATH + 1] = {0};	GetCurrentDirectoryA(MAX_PATH, curPath);	//strcat(curPath, "\\");	//strcat(curPath, "/");	FindAllFiles(curPath, true, UserFileFunc, UserFolderFunc);	//FindAllFiles(".", true, UserFileFunc, UserFolderFunc);	return 0;}


There are lots of ways to set it up though. I just happened to need function pointers for handling files and folders, but I've made other variations where I just had the function return a vector of files or worked directly with the paths in the function.
Quote:
// If we have a folder
if(FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)


Note to the OP, for all directories (compressed, hidden, system, archived ones, etc) rather than just "plain" ones change the == to your languages equivalent of bitwise and as dwFileAttributes is a bitmask of potentially several attributes.
Quote:Original post by adeyblue
Quote:
// If we have a folder
if(FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)


Note to the OP, for all directories (compressed, hidden, system, archived ones, etc) rather than just "plain" ones change the == to your languages equivalent of bitwise and as dwFileAttributes is a bitmask of potentially several attributes.


Thanks, I'll take that as a bug in my code since I handle everything else as a file if those bits were to be set. [smile]
Thankyou guys.

That code will help a lot :)
Quote:int main(int argc, char* argv[])
{
char curPath[MAX_PATH + 1] = {0};
GetCurrentDirectoryA(MAX_PATH, curPath);


"MAX_PATH should be enough for everyone".

Except when it isn't., and is actually 32k.
I've used something like this a couple times in the past.

void findFiles(string path, bool traverseSubDirectories){    WIN32_FIND_DATA fileData;                  HANDLE file = INVALID_HANDLE_VALUE;         string tmpPath= path + "\\*";     // find first file in directory    file = FindFirstFile(tmpPath.c_str(), &fileData);     // Couldn't find first file so display an error    if (file == INVALID_HANDLE_VALUE)    {        cout << "Error finding first file" << endl;        return;    }     // Loop through listing all the files    do    {        if (traverseSubDirectories)        {            if (fileData.cFileName[0] != '.')              {                if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)                  {                    string newPath = path + "\\" + fileData.cFileName;                     findFiles(newPath, traverseSubDirectories);                }            }        }                 if (fileData.dwFileAttributes ^ FILE_ATTRIBUTE_DIRECTORY)              cout << path + "\\" + fileData.cFileName << endl;        }    while (FindNextFile(file, &fileData) != 0);}
I would following the advice of SiCrane - boost::filesystem::recursive_directory_iterator - not only does it do exactly what you are looking for, but it's portable, and standards conforming, so you can use it in std algorithms :

#include <iostream>#include <algorithm>#include <boost/filesystem.hpp>int main(int argc, char** argv){    using namespace boost::filesystem;    recursive_directory_iterator i("C:\\"), end;    std::vector<path> files;    std::copy(i, end, std::ostream_iterator<path>(std::cout, "\n"));}
Quote:Original post by SiCrane
If using C++, you could just use boost::filesystem. If not, mentioning what programming language you are using might help.


QFE:
        namespace fs = boost::filesystem;        void recurseDirectories(const fs::path &path)        {            if (!fs::is_directory(path))            {                std::cout << "file: " << path << "\n";                return;            }            else                std::cout << "dir: " << path << "\n";            for (fs::directory_iterator i(path), end; i != end; ++i)                recurseDirectories(*i);        }        int main()        {            recurseDirectories("C:/WINDOWS");        }

This topic is closed to new replies.

Advertisement