Hello-World
Hello-World

Reputation: 9555

Why does FileSystemWatcher fire twice

why does the FileSystemWatcher fire twice? Is there an easy way to fix it? Surely if I update or edit the text file it should only fire once?

this link here http://weblogs.asp.net/ashben/archive/2003/10/14/31773.aspx says

  1. Events being raised twice - An event will be raised twice if an event handler (AddHander FSW.Created, AddressOf FSW_Created) is explicitly specified. This is because, by default, the public events automatically call the respective protected methods (OnChanged, OnCreated, OnDeleted, OnRenamed). To correct this problem, simply remove the explicit event handler (AddHandler ...).

What does "remove the explicit event handler" mean?

Imports System.IO

Public Class Form2

    Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed

        'this fires twice
        MessageBox.Show("test")

    End Sub

    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        FileSystemWatcher1.Path = "C:\Users\c\Desktop\test\"
        FileSystemWatcher1.NotifyFilter = NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName Or NotifyFilters.CreationTime

        FileSystemWatcher1.IncludeSubdirectories = False
        FileSystemWatcher1.Filter = "text.txt"

    End Sub

End Class

Upvotes: 8

Views: 9738

Answers (3)

Zvon
Zvon

Reputation: 11

Today i crashed in FileSystemWatcher and found this site. Suggested Thread.Sleep cannot completely eliminate problem. Tested with fast counter-integer. And is blocking UI. Most problematic is startup, it sliped trough at 5s. Then i set FileSystemWatcher1.EnableRaisingEvents = False immediately in TimerWatcherChanged.Tick and never enabled again... But surprisingly counter still catch up-to 4 events! I would like to share my solution, non-blocking, with adjustable Timer. Feedback is welcome.

Imports System.IO

Imports System.Diagnostics

Public Class Form1
  Dim fileName As String
  Dim Fsw_counter As Integer
  WithEvents TimerWatcherChanged As New Windows.Forms.Timer
  WithEvents TimerTest As New Windows.Forms.Timer

  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    TimerWatcherChanged.Interval = 100
    TimerTest.Interval = 100 : TimerTest.Start()
    TextBox1.Text = "C:\Downloads\New Text Document.txt"
    TextBox1.SelectionStart = TextBox1.Text.Length
    WatcherSetup()
  End Sub

  Sub WatcherSetup()
    fileName = TextBox1.Text
    FileSystemWatcher1.IncludeSubdirectories = False
    FileSystemWatcher1.Path = Path.GetDirectoryName(fileName)
    FileSystemWatcher1.Filter = Path.GetFileName(fileName)
    FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
    FileSystemWatcher1.EnableRaisingEvents = True
  End Sub

  Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
    WatcherSetup()
  End Sub

  Private Sub FileSystemWatcher1_Changed(sender As Object, e As FileSystemEventArgs) Handles FileSystemWatcher1.Changed
    If TimerWatcherChanged.Enabled = False Then
      TimerWatcherChanged.Enabled = True
      Fsw_counter += 1
      ' ***** Your WATCH Code put here... *****
    End If
  End Sub

  Private Sub TimerWatcherChanged_Tick(sender As Object, e As EventArgs) Handles TimerWatcherChanged.Tick
    TimerWatcherChanged.Enabled = False
  End Sub

  Private Sub TimerTest_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles TimerTest.Tick
    TextBox2.Text = "Changed: " & Fsw_counter
    If TimerWatcherChanged.Enabled = True Then
      TextBox2.BackColor = Color.Red
    Else
      TextBox2.BackColor = Color.LawnGreen
    End If
  End Sub
End Class

Upvotes: 0

Arrow
Arrow

Reputation: 2924

Update:

I have come up with 2 solutions. One uses Threads, and the other doesn't. Take your pick :-).

Without threading:

Imports System.IO

Public Class Form1
    Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
        Dim watcher As System.IO.FileSystemWatcher = sender
        watcher.EnableRaisingEvents = False

        'Do work here while new events are not being raised.
        MessageBox.Show("Test")

        watcher.EnableRaisingEvents = True 'Now we can begin watching for new events.

    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        FileSystemWatcher1.Path = "C:\Users\c\Desktop\test"
        FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
        FileSystemWatcher1.IncludeSubdirectories = False
        FileSystemWatcher1.Filter = "test.txt"


    End Sub

    Private Sub FileSystemWatcher_OnChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

    End Sub

End Class

This solution (without threading), sets the watcher.EnableRaisingEvents to False. It is after this point where you would normally process whatever files are affected (or changed). It then sets the EnableRaisingEvents back to True after your work is done.

With threading:

Imports System.IO

Public Class Form1
    Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
        FileSystemWatcher1.EnableRaisingEvents = False
        Threading.Thread.Sleep(250)
        FileSystemWatcher1.EnableRaisingEvents = True


        MessageBox.Show("test")


    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        FileSystemWatcher1.Path = "C:\Users\c\Desktop\test"
        FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
        FileSystemWatcher1.IncludeSubdirectories = False
        FileSystemWatcher1.Filter = "test.txt"


    End Sub

    Private Sub FileSystemWatcher_OnChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

    End Sub

End Class

This solution, although a bit hacky, does work. It disables checking for new changes/events for 250ms and then re-enables checking, based on the assumption that you won't been needing to check for a change every 250ms. I have tried almost everything that I could think of to get a real solution for you but this will work in the meantime.

Upvotes: 8

Frobzig
Frobzig

Reputation: 324

Check e.ChangeType. I imagine you're getting two different notifications. Perhaps LastAccess and LastModified. In which case, that's the expected behavior.

Upvotes: 1

Related Questions