The xxRecursiveSearch function provides a simple API to search for files and folders in a directory and its recursive subfolders. The provided callback routine is called for each file or folder that matches the given file spec. It aims to provide an easy-to-use and reusable solution to a problem that has typically involved messing around with FindNextFile, etc.
Samples
Code:
BOOL CALLBACK search_callback(LPTSTR szFileName, WIN32_FIND_DATA* pwfd, ULONG_PTR parameter)
{
_tprintf(TEXT("%s\n"), szFileName);
return TRUE;
}
int main(void)
{
// Return all files and folders under "C:\Windows\System32\"...
xxRecursiveSearch(TEXT("C:\\Windows\\System32\\"), TEXT("*"),
search_callback, 0, RSEARCH_RET_FILES | RSEARCH_RET_FOLDERS);
// Return all folders with "temp" in the path under "C:\Documents and Settings\"...
xxRecursiveSearch(TEXT("C:\\Documents And Settings\\"), TEXT("*temp*"),
search_callback, 0, RSEARCH_RET_FOLDERS);
getchar();
return 0;
}
Code:
/*
* Sample Two: Create a list of MP3 files under "C:\Documents and Settings\" in a string vector.
*/
BOOL CALLBACK search_callback(LPTSTR szFileName, WIN32_FIND_DATA* pwfd, ULONG_PTR parameter)
{
vector<string>* pVec = (vector<string>*) parameter; /* Retrieve the vector pointer from the parameter. */
pVec->push_back(szFileName); /* Add the file to the vector. */
return TRUE; /* Return TRUE to continue the search. */
}
int main(void)
{
vector<string> vecMP3s; /* Create a vector of MP3 files. */
/* Return only files which match the spec *.mp3" under "C:\Documents And Settings\"
* Pass a pointer to our vector so it can be used by the callback. */
xxRecursiveSearch(TEXT("C:\\Documents And Settings\\"), TEXT("*.mp3"),
search_callback, (ULONG_PTR) &vecMP3s, RSEARCH_RET_FILES);
/* Copy our vector to cout to make sure we succeeded. */
copy(vecMP3s.begin(), vecMP3s.end(), ostream_iterator<string>(cout, "\n"));
getchar();
return 0;
}
Implementation
Code:
/*
* Function to search a folder and subfolders for files and folders matching a given file spec.
* Calls a callback function for each matching file or folder that is found.
*/
RSEARCH_RESULT
WINAPI
xxRecursiveSearch(LPCTSTR szRootPath,
LPCTSTR szFileSpec,
PSEARCH_CALLBACK_FUNC pCallback,
ULONG_PTR parameter,
UINT flags)
{
WIN32_FIND_DATA wfd;
HANDLE hSearch;
RSEARCH_RESULT res = RSEARCH_SUCCESS;
TCHAR szSearch[MAX_PATH];
if (szRootPath == NULL || szFileSpec == NULL || pCallback == NULL)
{
return RSEARCH_INVALID_ARG;
}
StringCchCopy(szSearch, MAX_PATH, szRootPath);
StringCchCat(szSearch, MAX_PATH, TEXT("*"));
hSearch = FindFirstFile(szSearch, &wfd);
if (INVALID_HANDLE_VALUE == hSearch)
{
return RSEARCH_ERROR;
}
for (;;)
{
if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(flags & RSEARCH_RET_FILES))
{
TCHAR szFileName[MAX_PATH];
StringCchCopy(szFileName, MAX_PATH, szRootPath);
StringCchCat(szFileName, MAX_PATH, wfd.cFileName);
if (PathMatchSpec(szFileName, szFileSpec) &&
!pCallback(szFileName, &wfd, parameter))
{
res = RSEARCH_ABORTED;
break;
}
}
else if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
lstrcmp(wfd.cFileName, TEXT(".")) != 0 &&
lstrcmp(wfd.cFileName, TEXT("..")) != 0)
{
TCHAR szNewPath[MAX_PATH];
StringCchCopy(szNewPath, MAX_PATH, szRootPath);
StringCchCat(szNewPath, MAX_PATH, wfd.cFileName);
StringCchCat(szNewPath, MAX_PATH, TEXT("\\"));
if (flags & RSEARCH_RET_FOLDERS &&
PathMatchSpec(szNewPath, szFileSpec) &&
!pCallback(szNewPath, &wfd, parameter))
{
res = RSEARCH_ABORTED;
break;
}
if (!(flags & RSEARCH_NO_RECURSION) &&
(res = xxRecursiveSearch(szNewPath, szFileSpec, pCallback, parameter, flags)) == RSEARCH_ABORTED)
{
break;
}
}
if (!FindNextFile(hSearch, &wfd))
{
if (GetLastError() != ERROR_NO_MORE_FILES)
{
res = RSEARCH_ERROR;
}
break;
}
}
FindClose(hSearch);
return res;
}
Zip Contents
In the zip is the source file, header file, documentation, Dev-C++ project, more samples and a kitchen sink. xxRecursiveSearch has been tested with MSVC.NET, Dev-C++ and LCC-WIN32. Feel free to provide feedback, etc.