Hello Guys I am newbie here. After long hopeless search I wanted to post this problem:
I am developing a C++ program that recurses over all folders and sub-folders searching for a specific file types.
First the function FindFiles(string, string, bool)
works like a charm but the second form of it FindFiles(struct var)
Doesn't work fine: It doesn't iterate over all folders and sub-folders.
In fact I need the second form because as the fact that the search may be too long I need to create a thread with the API CreateThread
and pass my arguments struct
to it as LPVOID
.
#include <windows.h>
#include <string>
#include <iostream>
using namespace std;
// Arguments struct
struct args{
string strDir;
string strFilter;
bool bRecurse;
};
void FindFiles(string strDir, string strFilter, bool bRecurse);
void FindFiles(args);
int main(){
// FindFiles("D:", "*.mp3", true); // works fine
args ar;
ar.bRecurse = true;
ar.strDir = "D:";
ar.strFilter = "*.mp3";
FindFiles(ar); // doesn't work fine
cout << endl;
return 0;
}
void FindFiles(string strDir, string strFilter, bool bRecurse = true){
if(bRecurse)
FindFiles(strDir, strFilter, false);
strDir += "\\";
WIN32_FIND_DATA wfd;
string strFileFilter = strDir + (bRecurse ? "*" : strFilter);
HANDLE hFile = FindFirstFile(strFileFilter.c_str(), &wfd);
if(INVALID_HANDLE_VALUE == hFile)
return;
else{
if(!bRecurse)
cout << strDir + string(wfd.cFileName) << endl;
while(FindNextFile(hFile, &wfd) ){
if(!bRecurse)
cout << strDir + string(wfd.cFileName) << endl;
else{
if( (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0 &&
wfd.cFileName[0] != '.')
FindFiles(strDir + string(wfd.cFileName), strFilter, true);
}
}
// FindClose(hFile);
}
}
void FindFiles(args ar){
if(ar.bRecurse){
ar.bRecurse = false;
FindFiles(ar);
}
ar.strDir += "\\";
WIN32_FIND_DATA wfd;
string strFileFilter = ar.strDir + (ar.bRecurse ? "*" : ar.strFilter);
HANDLE hFile = FindFirstFile(strFileFilter.c_str(), &wfd);
if(INVALID_HANDLE_VALUE == hFile)
return;
else{
if(!ar.bRecurse)
cout << ar.strDir + string(wfd.cFileName) << endl;
while(FindNextFile(hFile, &wfd) ){
if(!ar.bRecurse)
cout << ar.strDir + string(wfd.cFileName) << endl;
else{
if( (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0 &&
wfd.cFileName[0] != '.'){
ar.strDir += string(wfd.cFileName);
ar.bRecurse = true;
FindFiles(ar);
}
}
}
FindClose(hFile);
}
}
Please pay a look at the second form because it is mine. I think there are some stupid mistakes there.
Any Help I do really appreciate it.
Your 1-param version of FindFiles()
doesn't recurse because it is modifying the ar.bRecurse
flag so it is always false when searching for sub-folders:
void FindFiles(args ar){
if(ar.bRecurse){
ar.bRecurse = false; // <-- modified!
FindFiles(ar);
}
...
// ar.bRecurse is still false here!
}
The 3-params version of FindFiles()
does not do that:
void FindFiles(string strDir, string strFilter, bool bRecurse = true){
if(bRecurse)
FindFiles(strDir, strFilter, false); // <-- bRecurse is not modified here!
...
// bRecurse is still the original value here!
}
The simplest solution is to reset ar.bRecurse
back to true after that initial FindFiles()
call:
void FindFiles(args ar){
if(ar.bRecurse){
ar.bRecurse = false; // <-- modified!
FindFiles(ar);
ar.bRecurse = true; // <-- reset here!
}
...
}
Or use a temp struct for that call:
void FindFiles(args ar){
if(ar.bRecurse){
args arTmp = ar; // <-- temp copy!
arTmp.bRecurse = false; // <-- modify the copy!
FindFiles(arTmp);
}
...
// ar.bRecurse is still the original value here!
}
Now, with that said, you don't really need the bRecurse
check at the top of the functions at all. All it is really doing is scanning the input folder for only matching files, before then scanning the folder again to find only sub-folders. You can accomplish the same logic by using a single search and locally caching the sub-folders to search next, eg:
#include <windows.h>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
// Arguments struct
struct args
{
string strDir;
string strExt;
bool bRecurse;
};
void FindFiles(const string &strDir, const string &strExt, bool bRecurse = true);
void FindFiles(const args &ar);
int main()
{
// FindFiles("D:", ".mp3", true);
args ar;
ar.strDir = "D:";
ar.strExt = ".mp3";
ar.bRecurse = true;
FindFiles(ar);
cout << endl;
return 0;
}
void FindFiles(const string &strDir, const string &strExt, bool bRecurse)
{
string strSearchDir = strDir;
vector<string> vDirs;
if (!strSearchDir.empty())
{
switch (strSearchDir[strSearchDir.size()-1])
{
case '\\':
case '/':
break;
default:
strSearchDir += "\\";
break;
}
}
WIN32_FIND_DATAA wfd = {};
HANDLE hFile = FindFirstFileA((strSearchDir + "*.*").c_str(), &wfd);
if (INVALID_HANDLE_VALUE == hFile)
return;
do
{
string strFilename(wfd.cFileName);
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strFilename == ".") || (strFilename == ".."))
continue;
if (bRecurse)
vDirs.push_back(strSearchDir + strFilename);
}
else
{
if (!strExt.empty())
{
if (strFilename.size() < strExt.size())
continue;
if (CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, strFilename.c_str()+(strFilename.size()-strExt.size()), strExt.size(), strExt.c_str(), strExt.size()) != 2)
continue;
}
cout << strSearchDir << strFilename << endl;
}
}
while (FindNextFile(hFile, &wfd));
FindClose(hFile);
if (bRecurse)
{
for(vector<string>::iterator iter = vDirs.begin(), end = vDirs.end(); iter != end; ++iter)
FindFiles(*iter, strExt, true);
}
}
void FindFiles(const args &ar)
{
FindFiles(ar.strDir, ar.strExt, ar.bRecurse);
}