为什么FileSystemWatcher会触发两次?

8
为什么FileSystemWatcher会触发两次?有没有简单的方法解决它?如果我更新或编辑文本文件,那么应该只会触发一次,对吗?
这个链接http://weblogs.asp.net/ashben/archive/2003/10/14/31773.aspx说:
事件被触发两次 - 如果明确指定了事件处理程序(AddHander FSW.Created, AddressOf FSW_Created),则事件将被触发两次。这是因为,默认情况下,公共事件会自动调用相应的受保护方法(OnChanged、OnCreated、OnDeleted、OnRenamed)。要解决这个问题,只需删除显式事件处理程序(AddHandler...)。
"删除显式事件处理程序"是什么意思?
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

你在何时遇到这个“意外”的行为?是在编辑文件、移动、删除或创建文件时吗? - Arrow
1
嗨,编辑文件是发生在什么时候的 - 谢谢。 - Hello-World
我更新了我的问题,并提供了可能的解决方案,至少可以作为检查的参考,这可能有助于缩小问题的范围。 - Arrow
你还应该在MessageBox警告后面去掉分号。 - Arrow
FSW对文件系统有非常低级别的视图。与文件相关联的有两个文件系统对象。文件和文件的目录条目。它们都会发生变化。这与某些程序快速更改文件并没有太大区别。 - Hans Passant
显示剩余2条评论
3个回答

8

更新:

我提出了两种解决方案。一个使用线程,另一个则不需要。您可以自行选择 :-).

不使用线程:

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

这个解决方案(不使用线程)将watcher.EnableRaisingEvents设置为False。在此之后,您通常会处理受影响(或更改)的任何文件。然后,在完成工作后,它会将EnableRaisingEvents重新设置为True。

使用线程:

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

这个解决方案虽然有点“hacky”,但是确实有效。它会在250毫秒内禁用新变化/事件的检查,然后重新启用检查,假定您不需要每250毫秒检查一次变化。我已经尝试了几乎所有我能想到的方法来为您找到一个真正的解决方案,但在此期间这个方法可以使用。


1
我之前在网络上的其他帖子中也看到过这种解决方案的使用。它对我也很有效。Microsoft 在 http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx 上进行了解释。 - JimDel
谢谢。我刚刚找到了一个更合适、更可行的解决方案,它非常相似,但不需要使用线程。我正在更新中。 - Arrow
谢谢,第一种技巧对我很有用。 我正在使用FileSystemWatcher在一个小的Windows服务中编译样式表,以供LiveReload使用,而多个更改事件会拖慢浏览器。 但是这个小调整已经解决了[FUOC](http://www.bluerobot.com/web/css/fouc.asp/)问题。 ;) - harpo

1

请检查 e.ChangeType。我想你可能会收到两个不同的通知。也许是 LastAccess 和 LastModified。如果是这样,那么这是预期的行为。


0

今天我在FileSystemWatcher中崩溃,然后找到了这个网站。建议的Thread.Sleep不能完全消除问题。使用快速计数器进行测试。并且会阻塞UI。最棘手的是启动,它在5秒钟内就溜走了。然后我在TimerWatcherChanged.Tick中立即设置了FileSystemWatcher1.EnableRaisingEvents = False,并且从未再次启用... 但令人惊讶的是,计数器仍然能够捕获多达4个事件! 我想分享我的解决方案,非阻塞,带可调节的计时器。欢迎反馈。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接