Stack Overflow bei rekursiver dateisuche



  • void findnext(wchar_t* sroot, HANDLE hFind, WIN32_FIND_DATA FindData) {
      while (FindNextFile(hFind, &FindData)) {
    
        if(wcscmp(FindData.cFileName,L".") != 0 && wcscmp(FindData.cFileName,L"..") != 0) {
          if(wcsstr(FindData.cFileName, L".mp3") != 0) {
            cout << FindData.cFileName << endl;
            wchar_t path[65535] = {'\0'};
            wcscat(path, sroot);
            wcscat(path, FindData.cFileName);
            vecAdd(path);
          }
          if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            HANDLE hFind2;
            wchar_t dir[65535] = {'\0'};
            wchar_t dir2[65535] = {'\0'};
            wcscat(dir, sroot);
            wchar_t* filename = FindData.cFileName;
            wcscat(dir, filename);
            wcscat(dir, L"\\");
            wcscat(dir2, dir);
            wcscat(dir, L"*.*");
            WIN32_FIND_DATA FindData2;
            hFind2 = FindFirstFile(dir, &FindData2);
            findnext(dir2, hFind2, FindData2);
          }
        }
      }
    }
    
    int main() {
      HANDLE hFind;
      WIN32_FIND_DATA FindData;
      hFind = FindFirstFile(A2BSTR("C:\\*.*"), &FindData);
      wchar_t* tx = (wchar_t*) FindData.cFileName;
      if(wcsstr(FindData.cFileName, L".mp3") != 0) {
    	printf("%s\n", tx);
        cout << FindData.cFileName << endl;
      }
      printf("%s\n\n", FindData.cFileName);
      findnext(A2BSTR("C:\\"), hFind, FindData);
      return 0;
    }
    

    Das ist der wichtige Teil des Codes. Seine Aufgabe ist erst einmal eine rekursive Dateisuche (Ordner + Unterordner). An sich funktioniert die Funktion: Sie erkennt Dateien und unterscheidet zwischen Dateien und Ordnern. Handelt es sich um einen Ordner, ruft sich die Funktion findnext() mit diesem als Parameter selbst auf. Auch das funktioniert.
    Das Problem ist, dass wenn das Programm bei "C:\Dokumente und Einstellungen\" ankommt, ruft sich die Funktion findnext() mit dem Parameter "C:\Dokumente und Einstellungen\All Users\." auf, so wie es auch vorgesehen ist, jedoch kommt es dann zum Stack-Overflow. 😕
    Trotz endloser Fehlersuche durch Debugging und Hilferufe an Google war es mir einfach nicht möglich, dieses Problem zu lösen.
    Ist wahrscheinlich nur eine Kleinigkeit, die ich einfach nicht sehen kann. Kann mir Jemand bitte einen Tipp, oder einen Stoß in die richtige Richtung geben?

    Schonmal danke für die Hilfe 🙂



  • path[65535] ist evtl echt zu viel, um es lokal auf dem stack zu lagern. Mit nur 16*64k hast Du das übliche Stack-Limit von 1M schon erreicht.
    Mach das mit new und optimalerweise lege nur so viel Speicher an, wie echt nötig.
    new ist zwar klitzeteuer, aber gegenüber Plattenzugriffen doch noch ultraschnell.
    edit: Bevor ich wieder gehauen werde: Ich nehme std::string oder solche Automatik-Klassen, um den aktuell zu durchsuchenden Pfad zu speichern, mit "new" meinte ich nur, das Zeug soll nicht auf den Stack.
    edit2: Nu habe ich beschrieben, wie ich es mache, aber das geht auf Deinen Fehler gar nicht ein. Wie er nach nur 3 Ebenen abkacken kann, kann ich mir nicht vorstellen. Bei mir klappt es immer.



  • Okay, hat das Problem gelöst. Danke vielmals !
    Interessant, das hatte ich gar nicht bedacht. Ich hatte nur im Kopf, dass auf dem Stack die Funktionsaufrufe zwischengespeichert werden - nicht auch deren Variablen, oder irre ich mich jetzt? 😕



  • völlig korerekt.
    Alle Funktionsaufrufe und alle lokale Variablen (außer lokal-static, aber das ist was anderes, nämlich im Prinzip global) landen auf dem Stack. (Übergabeparameter sind lokale Variablen der aufgerufenen Funktion.)



  • Okay, nochmals ein dickes Danke !

    P.S.: Thread kann als Gelöst markiert werden



  • Übrigens würde ich mir den FIND_DATA auch noch sparen mit

    while(toDo.isNotEmpty())
    {
       FindFirst
       do
       {
          if(verzeichnis)
             toDo.pushFront(name);
          else
             vecAdd
       }while(FindNext);
    }
    

    oder so.
    Also einen std::stack statt des Maschinenstacks. Vorteile: Keine 1M-Grenze, aber auch gar keine mehr. Und die Hoffnung, daß diverse Defragmentierer oder das Dateisystem selber netterweise dafür sorgen, daß in einem Verzeuichnis nebeneinanderliegende Einträge auch auf der Platte nebeneinander leigen und damit Kopfbewegeungen gespart werden.
    Immerhin kann der Prozessor 1000000-mal den Kopf schütteln bevor die Platte es einmal schafft.

    Ohne näher zu suchen, ob ich mich dafür schämen müßte, was von meiner Platte

    void scanDirectoryWithout(map<UInt64,IntrusiveDoubleLinkedRing<FileDescriptor>* >& files,wstring const& root,wstring const& exclude) {
    	wcout<<"rem scanning "<<root<<'\n';
    	int count=0;
    	vector<wstring> toDo;
    	toDo.push_back(root);
    	WIN32_FIND_DATAW wfd;
    	while (!toDo.empty()) {
    		wstring location=*toDo.rbegin();
    		toDo.pop_back();
    		if (location==exclude) continue;
    		wstring mask=location+L"*";
    //		wcout<<"scanning "<<mask<<'\n';
    		HANDLE hff=FindFirstFileW(mask.c_str(),&wfd);
    		if (hff!=INVALID_HANDLE_VALUE) {
    			do {
    				if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) {
    //						wcout<<"subbung "<<wfd.cFileName<<'\n';
    					if (wcscmp(wfd.cFileName,L".")!=0 and wcscmp(wfd.cFileName,L"..")!=0) {
    						toDo.push_back(location+wfd.cFileName+L'\\');
    					}
    				}
    				else {
    					++count;
    //						if(count%1024==0) wcout<<count<<" files\r";
    					UInt64 filesize=(UInt64(wfd.nFileSizeHigh)<<32)|wfd.nFileSizeLow;
    //						wcout<<filesize<<' '<<location+wfd.cFileName<<endl;
    					if(files[filesize]==0)
    						files[filesize]=new IntrusiveDoubleLinkedRing<FileDescriptor>;
    					files[filesize]->pushBack(new FileDescriptor(location+wfd.cFileName,wfd.dwFileAttributes));
    				}
    			} while (FindNextFileW(hff,&wfd));
    		}
    		FindClose(hff);
    	}
    	wcout<<"rem "<<count<<" files\n";
    }
    

Anmelden zum Antworten