// This function will search the specified directory for the specified // wildcard-string file name. iSubDirSearchDepth indicates the number of // subdirectories deep the search will go. For example, passing 0 will search // only the directory passed in pcPath, while passing 1 will search pcPath // and all its folders, but none of those folders’ folders. // Passing -1 will search all subdirectories of all subdirectories. // This function fails if you do not put a slash/backslash at the end of pcPath. INT SearchDir( const CHAR * pcPath, const CHAR * pcFile, INT iSubDirSearchDepth ) { // To search directories we use two searches. // One for the files the user wants and one to go into directories (if // iSubDirSearchDepth is not 0). // For the first search, create the final path based on what the user wants // to find. CHAR szFinal[MAX_PATH]; SNPrintF( szFinal, MAX_PATH, "%s%s", pcPath, pcFile ); // The structure to accept the return from Find*File(). WIN32_FIND_DATA wfdFind; // Now the actual search handle used to find the files the user wants. // This search will not go into subdirectories because not all subdirectories // will be matched by the wildcard string in pcFile. // In other words, if the user wants to find all files named “*.mp3”, this // search will not find all subdirectories unless they all end with “.mp3”, // which is highly unlikely. // So the search is broken into two parts. // First we create a scan string to match only what the user wants and we ignore // folders that are returned during this scan. // Then we create a second search which will simply replace pcFile with “*” and // will allow us to find all subdirectories. // We call this function recursively on all subdirectories, ignore the files // returned by that scan. // Keep track of how many we find. INT iFound = 0; // Get the handle for the search for the files the user wants. HANDLE hFind = FindFirstFile( szFinal, &wfdFind ); // If the handle is not invalid, continue. if ( hFind != INVALID_HANDLE_VALUE ) { while ( hFind ) { // “.” and “..” can be returned, so ignore them. if ( StrCmp( wfdFind.cFileName, "." ) != 0 && StrCmp( wfdFind.cFileName, ".." ) != 0 ) { // If it is a directory, ignore it for now. We will get to it in our second // search. Check that the FILE_ATTRIBUTE_DIRECTORY flag is NOT set. if ( (wfdFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) { // If we come here it is a file, not a directory. // Increase the found count and print the file name. iFound++; // Optionally print the file we found. // This will be very slow if many files are found! // Otherwise, replace this with your own code for operating on // wfdFind.cFileName. CHAR szFile[MAX_PATH]; SNPrintF( szFile, MAX_PATH, "%s%s", pcPath, wfdFind.cFileName ); PrintF( "%s", szFile ); } } // Get the next file. if ( !FindNextFile( hFind, &wfdFind ) || g_bSearchDirCancel ) { // No next file? Close the handle and leave the loop. FindClose( hFind ); hFind = NULL; break; } } } // hFind != INVALID_HANDLE_VALUE // If we are not to search subdirectories, we are done. if ( iSubDirSearchDepth == 0 ) { return iFound; } // If we come here, it means we are supposed to search subdirectories too. // The only way to get all subdirectories is to remake the scan string using // “*”. SNPrintF( szFinal, MAX_PATH, "%s*", pcPath ); hFind = FindFirstFile( szFinal, &wfdFind ); if ( hFind != INVALID_HANDLE_VALUE ) { while ( hFind ) { if ( StrCmp( wfdFind.cFileName, "." ) != 0 && StrCmp( wfdFind.cFileName, ".." ) != 0 ) { // This time we only care about directories if ( wfdFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // Recompile the string and pass it right back to this function in a // recursive call. CHAR szDir[MAX_PATH]; SNPrintF( szDir, MAX_PATH, "%s%s\\", pcPath, wfdFind.cFileName ); //PrintF( "%s", szDir ); // Call ourself and add the return. // Pass iSubDirSearchDepth - 1 since we are going one folder deeper. iFound += SearchDir( szDir, pcFile, iSubDirSearchDepth - 1 ); } } // Get the next file. if ( !FindNextFile( hFind, &wfdFind ) || g_bSearchDirCancel ) { FindClose( hFind ); hFind = NULL; break; } } } // hFind != INVALID_HANDLE_VALUE // We are done, and iFound contains the total matching files. return iFound; } // To add threading capabilities to SearchDir(), we need a wrapper function // that is compatible with the CreateThread() function. // The wrapper function will accept a pointer to a structure that we are // about to define. struct MHS_SEARCHDIRPARMS { const CHAR * pcPath; // Path to search. const CHAR * pcFile; // File mask. INT iSubDirSearchDepth; // Search depth. }; // Does that structure look familiar? Its members are the parameters we need // to get into SearchDir(). // Now create a function that accepts this structure as its only parameter. // Having one parameter of 4 bytes in size makes this function compatible // with CreateThread() (though any function can be called by CreateThread(), // it may have garbage in its parameters if not used correctly). // Note that this is really just a shortcut. Normally we would create this // function with a DWORD parameter and cast it to a MHS_SEARCHDIRPARMS * // inside the function, but this shortcut works the same and is faster too. INT SearchDir_SecondThread( MHS_SEARCHDIRPARMS * psdpParms ) { // Flip the cancel switch. g_bSearchDirCancel = FALSE; // Our only objective here is to call SearchDir() with the correct // parameters, however we have one other task to do here. // psdpParms is a pointer that was created by Malloc(), and it was not // freed with Free() because our main thread wants to make sure it is // alive long enough for this thread to use it (main thread below). // As a result, the main thread leaves it up to us to call Free() on // this pointer. // But first, get the actual result. Clear(); INT iRet = SearchDir( psdpParms->pcPath, psdpParms->pcFile, psdpParms->iSubDirSearchDepth ); /* while ( iRet != 0 && !g_bSearchDirCancel ) { Clear(); iRet = SearchDir( psdpParms->pcPath, psdpParms->pcFile, psdpParms->iSubDirSearchDepth ); }*/ // Getting the result is that easy! // We only needed a pointer to the MHS_SEARCHDIRPARMS object, and inside that // object we had all the other parameters we needed for the call to SearchDir. // But now we have to free the psdpParms object, so do it. Free( psdpParms->pcPath ); // Created with StrDup(), so Free() it. Free( psdpParms->pcFile ); // Created with StrDup(), so Free() it. Free( psdpParms ); PrintF( "Found %u files.", iRet ); // Pass back the value. // The main thread is not waiting for this thread to finish, so it can not // actually get this value, but it is proper to return it anyway. return iRet; } // Finally, this is called by the main thread to start the second thread with the // correct parameters. // It has the same paremeter list as SearchDir()! INT SearchDir_MainThread( const CHAR * pcPath, const CHAR * pcFile, INT iSubDirSearchDepth ) { // This function is quite simple. // First, make an object that can hold these parameters. MHS_SEARCHDIRPARMS * psdpParms = Malloc( sizeof( MHS_SEARCHDIRPARMS ) ); if ( psdpParms == NULL ) { return -1; } // If that worked, copy the strings to a place where they will not be // removed from memory while the second thread is working. psdpParms->pcPath = StrDup( pcPath ); psdpParms->pcFile = StrDup( pcFile ); psdpParms->iSubDirSearchDepth = iSubDirSearchDepth; // Call the second thread with the pointer to this structure as its parameter. HANDLE hThread = CreateThread( "SearchDir_SecondThread", (DWORD)psdpParms ); // If it failed, delete the structure ourselves. if ( !hThread ) { Free( psdpParms->pcPath ); // Created with StrDup(), so Free() it. Free( psdpParms->pcFile ); // Created with StrDup(), so Free() it. Free( psdpParms ); // No need to call CloseHandle() on a NULL handle. return -1; } // Coming here means the second thread is in motion, so let it delete // the structure on its own when it is done. // Our work here is done. // Close our handle on the thread and get out of here. CloseHandle( hThread ); // This does not stop the second thread. It just // releases our resources. PrintF( "Main thread closing." ); // 1 for success, -1 for error. return 1; } // Use this to cancel the search. BOOL g_bSearchDirCancel = FALSE; VOID On_HK_358() { g_bSearchDirCancel = TRUE; }