392 Stimmen

FileSystemWatcher Changed-Ereignis wird zweimal ausgelöst

Ich habe eine Anwendung, in der ich nach einer Textdatei suche, und wenn Änderungen an der Datei vorgenommen wurden, verwende ich die OnChanged eventhandler, um das Ereignis zu behandeln. Ich verwende die NotifyFilters.LastWriteTime aber trotzdem wird das Ereignis zweimal ausgelöst. Hier ist der Code.

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

In meinem Fall ist die OnChanged wird zweimal aufgerufen, wenn ich die Textdatei ändere version.txt und speichern Sie es.

308voto

Jørn Schou-Rode Punkte 36696

Ich befürchte, dass dies ein bekannter Fehler bzw. eine bekannte Eigenschaft des Systems ist. FileSystemWatcher Klasse. Dies steht in der Dokumentation der Klasse:

In bestimmten Situationen werden Sie feststellen, dass ein einzelnes Erstellungsereignis mehrere Created-Ereignisse erzeugt, die von Ihrer Komponente behandelt werden. Wenn Sie z. B. eine FileSystemWatcher-Komponente verwenden, um die Erstellung neuer Dateien in einem Verzeichnis zu überwachen, und sie dann testen, indem Sie Notepad verwenden, um eine Datei zu erstellen, werden möglicherweise zwei Created-Ereignisse erzeugt, obwohl nur eine einzige Datei erstellt wurde. Das liegt daran, dass Notepad während des Schreibvorgangs mehrere Dateisystemaktionen durchführt. Notepad schreibt in Stapeln auf die Festplatte, die den Inhalt der Datei und dann die Dateiattribute erstellen. Andere Anwendungen können auf die gleiche Weise vorgehen. Da FileSystemWatcher die Aktivitäten des Betriebssystems überwacht, werden alle Ereignisse, die diese Anwendungen auslösen, aufgefangen.

In diesem Textabschnitt geht es um die Created Ereignis, aber das Gleiche gilt auch für andere Dateiereignisse. In einigen Anwendungen können Sie dies vielleicht umgehen, indem Sie die NotifyFilter Eigenschaft, aber meine Erfahrung sagt, dass man manchmal auch manuell nach Duplikaten filtern muss (Hacks).

Vor einiger Zeit habe ich eine Seite mit ein paar FileSystemWatcher-Tipps . Das sollten Sie sich vielleicht ansehen.

163voto

David Brabant Punkte 38935

Ich habe dieses Problem mit der folgenden Strategie in meinem Delegaten "behoben":

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once asynchronously */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}

118voto

BaBu Punkte 1863

Alle duplizierten OnChanged Ereignisse aus dem FileSystemWatcher können erkannt und verworfen werden, indem die File.GetLastWriteTime Zeitstempel der fraglichen Datei. Zum Beispiel so:

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}

27voto

Deepashri Punkte 451

Hier ist meine Lösung, die mir geholfen hat, dass das Ereignis nicht mehr zweimal ausgelöst wird:

watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

Hier habe ich die NotifyFilter Eigenschaft nur mit Dateiname und Größe.
watcher ist mein Objekt von FileSystemWatcher. Hoffentlich hilft das.

11voto

Rémy Esmery Punkte 558

Hier ist mein Ansatz:

// Consider having a List<String> named _changedFiles

private void OnChanged(object source, FileSystemEventArgs e)
{
    lock (_changedFiles)
    {
        if (_changedFiles.Contains(e.FullPath))
        {
            return;
        }
        _changedFiles.Add(e.FullPath);
    }

    // do your stuff

    System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
    timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
    {
        lock (_changedFiles)
        {
            _changedFiles.Remove(e.FullPath);
        }
    };
   timer.Start();
}

Mit dieser Lösung habe ich das Problem bei einem Projekt gelöst, bei dem ich die Datei als Anhang in einer E-Mail versendet habe. Auch mit einem kleineren Zeitintervall lässt sich das zweimalige Auslösen des Ereignisses leicht vermeiden, aber in meinem Fall war 1000 in Ordnung, da ich lieber ein paar Änderungen verpasste als das Postfach mit > 1 Nachricht pro Sekunde zu überfluten. Zumindest funktioniert es gut, wenn mehrere Dateien zur gleichen Zeit geändert werden.

Eine andere Lösung, die ich mir überlegt habe, wäre, die Liste durch ein Wörterbuch zu ersetzen, das die Dateien ihren jeweiligen MD5-Werten zuordnet, so dass man kein willkürliches Intervall wählen müsste, da man den Eintrag nicht löschen, sondern seinen Wert aktualisieren müsste, und seine Arbeit abbrechen könnte, wenn er sich nicht geändert hat. Das hat den Nachteil, dass das Wörterbuch mit der Anzahl der überwachten Dateien wächst und immer mehr Speicher verbraucht, aber ich habe irgendwo gelesen, dass die Anzahl der überwachten Dateien vom internen Puffer des FSW abhängt, also vielleicht nicht so kritisch. Ich weiß nicht, wie sich die MD5-Berechnungszeit auf die Leistung deines Codes auswirkt, aber sei vorsichtig =\

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X