FileSystemWatcher的Changed事件被触发两次

390

我有一个应用程序,在其中寻找一个文本文件,如果文件有任何更改,我将使用 OnChanged 事件处理程序来处理该事件。我正在使用 NotifyFilters.LastWriteTime 但是仍然会触发两次事件。以下是代码。

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)
{
   .......
}
在我的情况下,当我改变文本文件version.txt并保存它时,OnChanged被调用了两次。

这是一个解决方法,但应该根据解决方法的质量来评判。跟踪更改效果完美且简单。OP正在寻找一种抑制重复事件的方法,下面的回复就提供了这种方法。https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.created.aspx解释了多个事件可能是由于反病毒软件或其他“复杂的文件系统问题”引起的(听起来只是借口)。 - Tyler Montney
2
我最近打开了这个问题 https://github.com/Microsoft/dotnet/issues/347 - Stephan Ahlf
3
我创建了一个类,可以帮助您获取一个事件。您可以从https://github.com/melenaos/FileSystemSafeWatcher获取代码。 - Menelaos Vergis
上面Menelaos Vergis提供的解决方案百分之百有效。 - undefined
45个回答

-1

一直在寻找答案,但最终只想到了一个不太好的解决方案。由于我的事件触发两次,第二个动作没有任何效果。

       $count = 1
       $action = { 
            if($count -eq 1){                  
                #DO SOMETHING
                $count = 2 
            }else{
                $count = 1
            }
        }  

-1
无论事件被触发多少次都没关系,因为它可以在下一次更改时再次触发。您最好控制输出,只显示一次。例如,我使用该事件来显示一个隐藏的按钮,所以我只需以这种方式检查输出即可。
if (!button1.Visible) button1.Visible = true;

对于一些无法检查存在性的操作,检查时间差异比使用计时器更容易且代码量更少。

DateTime last_trigger;
private void OnChanged(object sender, FileSystemEventArgs e)
{
    DateTime now = DateTime.Now;
    TimeSpan triggered = now - last_trigger;
    last_trigger = now;
    if (triggered.TotalMilliseconds < 1000) return;
    // do something
}

-2
如果您注册了OnChanged事件,那么在更改文件之前删除被监视的文件可能会起作用,只要您只需要监视OnChange事件即可。

2
如果我理解你的意思正确,你建议在更改文件之前删除现有文件,并且这应该只引发1个更改事件。不幸的是,这样做行不通。 - René

-6

好的,这是我的解决方案,如何只触发一次事件:

FileSystemWatcheк watcher = new FileSystemWatcher();

//'path' - path to the file that has been modified.
watcher.Changed += (s, e) => FileChanged(path);

这里是FileChanged的实现

//count is our counter to triger when we can raise and when not.
private int count = 0;
private void FileChanged(string path)
{
   if (count % 2 == 0)
     {
       //code here
     }

     count ++;
}

1
这个实现假设您将始终获得确切的2个通知。这并不保证比“一个用户操作”==“一个通知”的原始假设更可靠。 - Damien_The_Unbeliever
可能会引发3次,这种情况下您将处理两次。如果它总是恰好引发两次,他们会修复它,使其仅引发一次事件。 - Damien_The_Unbeliever
确切地说,这是因为框架引发了两次“变更”事件,而我在每次引发事件时都不需要处理它两次。通过这种方法,确保“一个用户操作”==“一个通知”。 - Julian Ustiyanovych
你好,Damien :) 在我的情况下,它被触发了两次。情况是,用户在我们的应用程序中打开文件并在notepad++中编辑它。当用户完成编辑后,保存文件,然后更改事件会触发两次。我在网络上找到了这个已知问题,因此我提出了这个解决方案,当然,如果他们修复了这个问题,我就可以跳过这段代码。但现在还好。你认为呢? - Julian Ustiyanovych
8
针对您测试过的Notepad ++版本和特定用户场景,这是可以的。但一般存在这样的问题,即事件将被触发未知次数(基于其他涉及的应用程序、应用程序的版本、所采取的操作等变量)- 通常情况下无法假定事件将被触发固定次数。 - Damien_The_Unbeliever
正如Damien所指出的那样,这种解决方案可能在Julian狭窄的情境中起作用,但是在一般情况下它并不可靠。例如,如果同时更改了2个不同的文件,它将会中断。(这可能发生在网络共享中或者将多个文件复制到观察文件夹中) - René

-6

让它简单明了,定义一个全局变量var1 = true

Private Sub FileWatchman_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileWatchman.Changed
   If var1 = true 
       your logic goes here
       var1 = false
   Else
       var1 = true 
   End If
End Sub

2
本质上,它在处理“已更改”事件时忽略了“已更改”事件,仅在此时忽略。因此,如果您的处理足够快并且在第二个事件被触发之前完成,则不会真正解决问题,两个事件都将被处理。此外,如果同时更改2个文件,则此实现将导致问题。(可能会在网络共享上发生或者多个文件被复制到监视文件夹中) - René

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