C# FileSystemWatcher Changed 事件被引发两次

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1764809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 20:37:07  来源:igfitidea点击:

FileSystemWatcher Changed event is raised twice

c#filesystemwatcher

提问by user214707

I have an application where I am looking for a text file and if there are any changes made to the file I am using the OnChangedeventhandler to handle the event. I am using the NotifyFilters.LastWriteTimebut still the event is getting fired twice. Here is the code.

我有一个应用程序,我正在寻找一个文本文件,如果对文件进行了任何更改,我将使用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)
{
   .......
}

In my case the OnChangedis called twice, when I change the text file version.txtand save it.

在我的情况下OnChanged,当我更改文本文件version.txt并保存时,它被调用了两次。

回答by J?rn Schou-Rode

I am afraid that this is a well-known bug/feature of the FileSystemWatcherclass. This is from the documentation of the class:

恐怕这是FileSystemWatcher该类的一个众所周知的错误/功能。这是来自该类的文档:

You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.

您可能会注意到,在某些情况下,单个创建事件会生成多个由您的组件处理的 Created 事件。例如,如果您使用 FileSystemWatcher 组件监视目录中新文件的创建,然后使用记事本创建文件对其进行测试,您可能会看到生成了两个 Created 事件,即使只创建了一个文件。这是因为记事本在写入过程中会执行多个文件系统操作。记事本批量写入磁盘,创建文件内容和文件属性。其他应用程序可以以相同的方式执行。因为 FileSystemWatcher 监视操作系统活动,所以这些应用程序触发的所有事件都将被拾取。

Now this bit of text is about the Createdevent, but the same thing applies to other file events as well. In some applications you might be able to get around this by using the NotifyFilterproperty, but my experience is says that sometimes you have to do some manual duplicate filtering (hacks) as well.

现在这段文字是关于Created事件的,但同样的事情也适用于其他文件事件。在某些应用程序中,您可能可以通过使用该NotifyFilter属性来解决此问题,但我的经验是有时您还必须进行一些手动重复过滤(黑客攻击)。

A while ago I bookedmarked a page with a few FileSystemWatcher tips. You might want to check it out.

不久前,我为一个包含一些FileSystemWatcher 提示的页面添加了书签。你可能想检查一下。

回答by Wil P

I have changed the way I monitor files in directories. Instead of using the FileSystemWatcher I poll locations on another thread and then look at the LastWriteTime of the file.

我改变了监视目录中文件的方式。我没有使用 FileSystemWatcher 在另一个线程上轮询位置,然后查看文件的 LastWriteTime。

DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);

Using this information and keeping an index of a file path and it's latest write time I can determine files that have changed or that have been created in a particular location. This removes me from the oddities of the FileSystemWatcher. The main downside is that you need a data structure to store the LastWriteTime and the reference to the file, but it is reliable and easy to implement.

使用此信息并保留文件路径的索引和最新写入时间,我可以确定已更改或已在特定位置创建的文件。这使我摆脱了 FileSystemWatcher 的奇怪之处。主要缺点是您需要一个数据结构来存储 LastWriteTime 和对文件的引用,但它可靠且易于实现。

回答by MatteKarla

You could try to open it for write, and if successful then you could assume the other application is done with the file.

您可以尝试打开它进行写入,如果成功,那么您可以假设其他应用程序已完成该文件。

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

Just opening it for write appears not to raise the changed event. So it should be safe.

只是打开它进行写入似乎不会引发已更改的事件。所以应该是安全的。

回答by David Brabant

I've "fixed" that problem using the following strategy in my delegate:

我已经在我的委托中使用以下策略“修复”了这个问题:

// 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;
   }
}

回答by BaBu

Any duplicated OnChangedevents from the FileSystemWatchercan be detected and discarded by checking the File.GetLastWriteTimetimestamp on the file in question. Like so:

通过检查相关文件上的时间戳,可以检测并丢弃OnChanged来自 的任何重复事件。像这样:FileSystemWatcherFile.GetLastWriteTime

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
}

回答by guy yogev

if you register to the OnChanged event, then by deleting the monitored file before changing it might work, as long as you only need to monitor the OnChange event..

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

回答by Ikon

My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :

我的场景是我有一台带有 Linux 服务器的虚拟机。我正在 Windows 主机上开发文件。当我更改主机上文件夹中的某些内容时,我希望将所有更改上传,并通过 Ftp 同步到虚拟服务器。这就是我在写入文件时消除重复更改事件的方法(它也标记包含要修改的文件的文件夹):

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.

主要是我创建了一个哈希表来存储文件写入时间信息。然后,如果哈希表具有修改的文件路径,并且它的时间值与当前通知的文件更改相同,那么我知道它是事件的副本并忽略它。

回答by Rémy Esmery

Here's my approach :

这是我的方法:

// 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();
}

This is the solution I used to solve this issue on a project where I was sending the file as attachment in a mail. It will easily avoid the twice fired event even with a smaller timer interval but in my case 1000 was alright since I was happier with missing few changes than with flooding the mailbox with > 1 message per second. At least it works just fine in case several files are changed at the exact same time.

这是我用来在一个项目中解决此问题的解决方案,在该项目中我将文件作为邮件附件发送。即使使用较小的计时器间隔,它也可以轻松避免两次触发事件,但在我的情况下,1000 是可以的,因为我更高兴缺少一些更改,而不是每秒用 > 1 条消息淹没邮箱。至少它可以在几个文件同时更改的情况下正常工作。

Another solution I've thought of would be to replace the list with a dictionary mapping files to their respective MD5, so you wouldn't have to choose an arbitrary interval since you wouldn't have to delete the entry but update its value, and cancel your stuff if it hasn't changed. It has the downside of having a Dictionary growing in memory as files are monitored and eating more and more memory, but I've read somewhere that the amount of files monitored depends on the FSW's internal buffer, so maybe not that critical. Dunno how MD5 computing time would affect your code's performances either, careful =\

我想到的另一个解决方案是用字典将文件替换为它们各自的 MD5,这样您就不必选择任意间隔,因为您不必删除条目而是更新其值,并且如果它没有改变,取消你的东西。它的缺点是,随着文件受到监控,内存中的字典会增长,并且占用的内存越来越多,但我在某处读到过,监控的文件数量取决于 FSW 的内部缓冲区,所以可能不是那么重要。也不知道 MD5 计算时间会如何影响您的代码性能,小心 =\

回答by Julian Ustiyanovych

Well, here is my solution how to raise an event only once:

好吧,这是我如何仅引发一次事件的解决方案:

FileSystemWatcheк watcher = new FileSystemWatcher();

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

here is implementation of FileChanged

这是 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 ++;
}

回答by Deepashri

Here is my solution which helped me to stop the event being raised twice:

这是我的解决方案,它帮助我阻止了两次引发的事件:

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

Here I have set the NotifyFilterproperty with only Filename and size.
watcheris my object of FileSystemWatcher. Hope this will help.

在这里,我只设置了NotifyFilter文件名和大小的属性。
watcher是我的 FileSystemWatcher 对象。希望这会有所帮助。