FileSystemWatcher不报告锁定文件中的更改

时间:2010-08-22 08:52:18

标签: c# filesystemwatcher

我正在使用FileSystemWatcher监视文件夹,如下所示:

watcher = new FileSystemWatcher(folder);
watcher.NotifyFilter = NotifyFilters.Size;
watcher.Changed += changedCallback;

当我在该文件夹中的记事本中打开一个新文件并保存时,我会收到通知。如果我继续写作然后保存,我会收到通知。如果我保存它关闭文件,我会收到通知。正是我想要的。

然而,事实证明,如果我在该文件夹中创建一个文件并将其共享模式设置为FileShare.Read,然后我写入它,则在文件关闭之前我不会收到任何通知。另一种解决方法是打开文件(例如在记事本中),这显然会导致其状态更新,然后我的监控应用程序会收到通知。另一种解决方法是我可以在Windows资源管理器中执行刷新,这再次导致文件状态更新。

有趣的是,如果我在进行更改时查看Windows资源管理器,我会注意到:

  1. 如果共享文件以供阅读&写作时,只要我保存它,它的大小就会立即在Windows资源管理器中更新。
  2. 如果共享文件仅供读取,则不会在Windows资源管理器中立即更新其大小,除非我手动刷新窗口。
  3. 因此,我的监控应用程序似乎与Windows资源管理器共享相同的行为。我正在考虑运行一个只扫描文件夹中文件的线程,但我想知道在这种情况下是否还有更优雅的事情要做。

    BTW,我使用的是Win7,我也不确定其他Windows版本是否会出现此问题。

    谢谢!

    编辑:在C ++中使用ReadDirectoryChanges可以得到完全相同的结果。实现我之前谈到的线程并没有帮助。我想知道Windows资源管理器中的F5究竟是做什么的,因为它确实会导致报告更改。

6 个答案:

答案 0 :(得分:10)

问题的解决方案不是打开文件,而是实际从它们读取。它甚至可以读取一个字节,Windows缓存机制会将文件的内容写入磁盘,从而允许您读取它们。

我最终实现了一个遍历所有文件的线程,打开它们并从中读取一个字节。这导致它们发生更改,并在FileSystemWatcher对象中触发了该事件。

Windows资源管理器F5的工作原理是,Windows实际上是读取文件的内容以显示某些扩展内容(例如,缩略图)。一旦读取文件,缓存首先写入磁盘,在FSW中触发事件。

答案 1 :(得分:4)

是的,资源管理器使用与FileSystemWatcher使用的完全相同的API。你发现只有一个ReadDirectoryChangesW()。

您发现强烈建议Win7正在优化更新文件目录条目所需的磁盘写入。在文件关闭时将其延迟到最后一刻。这个观察与用户在Win7的RTM版本中发现的关键错误之间存在有趣的关联。更新有时根本不会发生。这个bug随机发生,但不经常发生,我自己只在机器上看过一次。无论如何,明知。

详细信息在this thread中(请注意服务器速度很慢)。今天仍然无法应用所有Win7更新。

嗯,有趣的花絮但与你的问题没有密切关系。您需要更改代码以适应操作系统的工作。

答案 2 :(得分:4)

在编写Windows服务时遇到了同样的FileSystemWatcher问题。该服务是用.NET编写的,并使用FileSystemWatcher来监视第三方产品生成的日志文件。在测试期间,我在第三方产品中执行了操作,我知道强制日志条目被写入,但是我的服务断点从未被触发,直到我在记事本中打开目标日志文件或刷新我在Windows资源管理器中的视图。

我的解决方案是在创建FileSystemWatcher的同时创建一个FileInfo实例(我们将调用该fileInfoInstance)。每当我启动或停止我的FileSystemWatcher时,我也会启动或停止一个System.Threading.Timer,其回调每N毫秒调用一次fileInfoInstance.Refresh()。似乎fileInfoInstance.Refresh()刷新缓冲区/写入缓存,并允许FileSystemWatcher事件以与在资源管理器中命中F5相同的方式引发。

有趣的是(并且遗憾地)足够的fileInfoInstance.Directory.Refresh()没有完成相同的结果,所以如果你正在观看多个文件,即使它们都在同一个目录中并被同一个观察者观看,对于你正在观看的每个文件,你需要一个FileInfo实例,你的计时器回调应该用每个“tick”刷新它们...

快乐的编码。

布赖恩

答案 3 :(得分:2)

看起来文件数据已缓存且未实际写入。当您向文件写入内容时,首先使用特定文件系统IRP(驱动程序请求)将数据放入缓存。现在,当数据实际写入磁盘时,会发送另一个IRP。可以(这是猜测)FileSystemWatcher仅捕获第二个IRP类型。

解决方案(如果可能)是在您正在编写的文件上调用Flush。如果您正在跟踪其他应用程序所做的更改,当然事情会变得更加复杂。

答案 4 :(得分:1)

你确实需要一个可以打开所有文件并从中读取一个字节的线程,我的程序在Windows 7而不是XP上运行后停止工作,我使用了以下代码

private void SingleByteReadThread(object notUsed)
    {
       while (true)
      {
         foreach (FileInfo fi in new DirectoryEnumerator(folderPath))
                  {
                      using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                          fs.ReadByte();    
                  }

          Thread.Sleep(TimeSpan.FromSeconds(2));
      }
  }

DirectoryEnumerator是我自己的类

答案 5 :(得分:0)

您必须调用FileStream Flush()方法来编写文件更改。