Recurrsivly iterating through directories
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.
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.
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.
#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 adeyblueQuote:
// 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]
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
Popular Topics
Advertisement