Dateisystem überwache



  • Ich habe vor eine Bibliothek zu erstellen, die ich für ein anderes Projekt verwende, mit dem ich das Dateisystem auf Änderungen überwachen kann. Mir geht es um Events wie Datei erstellt, Datei gelöscht oder Datei geändert. Dazu habe ich mich mal im Internet schlau gemacht.
    Da ich Qt5 verwende habe ich zuerst gesehen, dass es eine Klasse namens QFilesystemWatcher gibt. Leider hat die Klasse nur 2 Events die ausgelöst werden wenn eine Datei oder ein Verzeichnis geändert wurde (betrifft erstellen, löschen und ändern). Ich bräuchte aber Events zu allen Aktionen. Deswegen hab ich unter Windows die Windows API ReadDirectoryChangesW Funktion gefunden, aber ich habe teilweise gelesen, dass nur ältere Betriebssysteme unterstützt werden, wie Windows XP oder Windows NT.

    Meine Frage ist jetzt, gibt es eine sichere Funktion unter Windows Vista und drüber mit der ich das bewerkstelligen kann? Kennt jemand vielleicht eine Bibliothek die das Problem schon gelöst und vereinfacht hat?

    Ich würde mich über jegliche Art von Tipps freuen.

    P.S.: Ich möchte das später unter Linux auch hinbekommen, aber da habe ich schon Inotify gefunden. Mir gehts erstmal nur um neuere Window Versionen.

    Vielen Danke



  • ReadDirectoryChanges(..) sollte eigentlich auch für Windows > XP verfügbar sein. Wo hast du das gelesen, dass nur bis XP geht?



  • Ich hab das irgendwo auf CodeProject gelesen. Aber ich glaube das bezog sich doch nur auf seine eigene Bibliothek.

    Hab nun mal weitergesucht und das hier gefunden https://code.google.com/archive/p/simplefilewatcher/

    ich werde das mal ausprobieren. Sieht auf den ersten Blick ganz nett aus. Hoffentlich funktioniert das auch einwandfrei.

    Da ich ja Qt5 verwende, kann ich die QFilesystemWatcher Klasse nicht erweitern? Ist aber wahrscheinlich zu aufwendig. Hatte den Code mal durchgesehen und konnte leider die Events nicht finden.



  • Also ich würde, wenn Du eh schon Qt5 verwendest, auch den QFileSystemWatcher verwenden, um später bei Linux keine Probleme zu haben. Qt kappselt dir das ja schon bereits.

    Verstehe ich Dich richtig, dass es Dich stört nicht folgende Events vorzufinden?

    void fileRenamed(const QString& old, const QString& new);
    void fileRemoved(const QString& file);
    // ...
    

    Ich würde diese Logik einfach selbst implementieren. Die Suche nach einer passende Windows Interface und dann noch für Linux zu finden, kostet Dich bestimmt mehr Zeit als die Implementierung.

    Beispielhaft + Pseudo

    void fileChanged(const QString& file) {
      if( !QFile::exists( file ) ) {
        emit fileRemoved(file);
        return;
      }
    
      // pseudo
      if( Änderungsdatum vom Ordner == Änderungsdatum von Datei ) {
        emit fileAdded(file);
        return;
      }
    
      emit fileChanged(file);
    }
    

    Geht natürlich nur, wenn Linux sich gleich verhält wie Windows Dateisystem ... sonst funktioniert das mit den Änderungsdaten nicht ...



  • Ja muss ich mal schauen. Wenn ich den QFilesystemWatcher nutzen könnte wäre das schon ziemlich geil



  • Ich bins nochmal. Hab da nochmal eine Frage und wollte dazu keinen neuen Beitrag eröffnen. Bisher habe ich verschiedene fertige Klassen ausprobiert um meine Wünsche zu erfüllen, aber leider waren diese nie zufrieden stellend. Nun habe ich gedacht, dann schreibst du dir halt von Grund auf alles selbst.

    Nun habe ich mir die Windows API nochmal angeguckt und mir ist aufgefallen, dass ich anscheinend keine Änderungen bei Verzeichnissen auffangen kann. Sprich wenn ein Verzeichnis erstellt, gelöscht oder umbenannt wird.

    In der Windows API habe ich die FILE_NOTIFY_INFORMATION structure gefunden, aber diese beinhaltet nur Änderungen von Dateien und keine Verzeichnissen. So wie ich das verstanden habe, landen Antworten von ReadDirectoryChangesW in dieser Struktur. Aber in FILE_NOTIFY_INFORMATION werden nur Dateien behandelt.

    Das ist irgendwie komisch. Konnte bisher auch nichts anderes finden.

    Hier mal ein Link
    https://msdn.microsoft.com/en-us/library/windows/desktop/aa364391(v=vs.85).aspx

    Kann mir jemand weiterhelfen?



  • Ich habe es nicht ausprobiert, gehe aber davon aus, dass es auch für Directories funktioniert. Am besten einfach kurz in einem Testprogramm ausprobieren.



  • Ich habe diesen Code hier verwendet und da benutzt er es auch. Damit habe ich es getestet und es ging nicht. https://github.com/apetrone/simplefilewatcher/blob/master/source/FileWatcherWin32.cpp
    Einfach mal ausprobieren ist lustig :). Bin noch dabei das zu lernen.



  • Doch, ReadDirectoryChangesW(..) sollte auch für Änderungen an Verzeichnissen funktionieren. Vielleicht fehlt in dem simplefilewatcher noch das eine oder andere Filter-Flag.

    Hier wäre noch ein Beispiel, welches du dir ansehen könntest: http://www.codeproject.com/Articles/950/CDirectoryChangeWatcher-ReadDirectoryChangesW-all

    // Edit
    Hier noch ein kleines Bsp. von mir. Es ist sehr unvollständig und enthält ein paar Hacks für die man bessere Lösungen suchen sollte - ausserdem arbeitet es synchron, was vermutlich nicht optimal ist. Aber es zeigt, dass ReadDirectoryChangesW(..) grundsätzlich auch für Verzeichnisse funkioniert:

    #include <iostream>
    #include <string>
    
    #include <Windows.h>
    
    class scoped_file_handle
    {
    public:
        explicit scoped_file_handle(HANDLE handle)
            : m_handle(handle)
        {}
    
        ~scoped_file_handle()
        {
            if (valid())
            {
                CloseHandle(m_handle);
            }
        }
    
        scoped_file_handle(const scoped_file_handle&) = delete;
        scoped_file_handle& operator=(const scoped_file_handle&) = delete;
        scoped_file_handle(scoped_file_handle&&) = delete;
        scoped_file_handle& operator=(scoped_file_handle&&) = delete;
    
        bool valid() const
        {
            return m_handle != INVALID_HANDLE_VALUE;
        }
    
        HANDLE get() const
        {
            return m_handle;
        }
    
    private:
        HANDLE m_handle;
    };
    
    std::string get_file_name(const FILE_NOTIFY_INFORMATION& fni)
    {
        auto char_count = fni.FileNameLength / sizeof(fni.FileName[0]);
        std::wstring wstr(fni.FileName, char_count);
        return std::string(begin(wstr), end(wstr));
    }
    
    int main()
    {
        //char current_dir[MAX_PATH] = {};
        //GetCurrentDirectory(MAX_PATH, current_dir);
    
        auto dir_path = R"(tmp)";
    
        scoped_file_handle hDirectory(
            CreateFile(
                dir_path,
                FILE_LIST_DIRECTORY | GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_BACKUP_SEMANTICS,
                NULL));
    
        if (!hDirectory.valid())
        {
            std::cerr << "CreateFile(..) failed, error: " << GetLastError() << std::endl;
            return 1;
        }
    
        const auto buffer_size = sizeof(FILE_NOTIFY_INFORMATION) + MAX_PATH;
        char buffer[buffer_size];
        DWORD bytes_returned = 0;
    
        auto result = ReadDirectoryChangesW(
            hDirectory.get(),
            &buffer,
            buffer_size,
            FALSE,
            FILE_NOTIFY_CHANGE_FILE_NAME |
            FILE_NOTIFY_CHANGE_DIR_NAME |
            FILE_NOTIFY_CHANGE_ATTRIBUTES |
            FILE_NOTIFY_CHANGE_SIZE |
            FILE_NOTIFY_CHANGE_LAST_WRITE |
            FILE_NOTIFY_CHANGE_LAST_ACCESS |
            FILE_NOTIFY_CHANGE_CREATION |
            FILE_NOTIFY_CHANGE_SECURITY,
            &bytes_returned,
            NULL,
            NULL);
    
        if (result == 0)
        {
            std::cerr << "ReadDirectoryChangesW(..) failed, error: " << GetLastError() << std::endl;
            return 1;
        }
    
        auto fni = reinterpret_cast<const FILE_NOTIFY_INFORMATION*>(buffer);
        std::cout << "ReadDirectoryChangesW(..) succeeded:\n";
        std::cout << "  fni->NextEntryOffset = " << fni->NextEntryOffset << "\n";
        std::cout << "  fni->Action = " << fni->Action << "\n";
        std::cout << "  fni->FileNameLength = " << fni->FileNameLength << "\n";
        std::cout << "  fni->FileName = " << get_file_name(*fni) << "\n";
    }
    

Log in to reply