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

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

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

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

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

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

  1. 如果共享文件进行读写,则在保存文件后,它的大小将立即在Windows资源管理器中更新。
  2. 如果共享文件仅供读取,则不会在Windows资源管理器中立即更新其大小,除非我手动刷新窗口。

所以我的监控应用程序似乎与Windows资源管理器共享相同的行为。 我正在考虑运行一个只扫描文件夹中文件的线程,但我想知道在这种情况下是否还有更优雅的事情要做。

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

谢谢!

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

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

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

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

是的,Explorer使用与FileSystemWatcher使用的完全相同的API。 你发现只有一个ReadDirectoryChangesW()。

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

细节在这个post中 (小心服务器非常慢)。 今天仍然无法应用所有Win7更新。

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

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

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

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

快乐的编码。

布赖恩

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

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

你确实需要一个可以打开所有文件并从中读取一个字节的线程,我的程序在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是我自己的类

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