FileSystemWatcher对于子目录中的更改不可靠

时间:2018-03-29 09:45:10

标签: c# .net filesystemwatcher

我目前正在为OpenFOAM输出文件实现文件内容监视器。这些文件由OpenFOAM在Unix环境中编写,并由我的应用程序在Windows环境中使用。

请考虑我的第一个工作观察程序用于收敛文件(这些文件在解决方案的每次迭代后都会更新):

FileSystemWatcher watcher;
watcher = new FileSystemWatcher(WatchPath, "convergenceUp*.out");
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Attributes | NotifyFilters.FileName | NotifyFilters.Size;
watcher.Changed += Watcher_Changed;
watcher.EnableRaisingEvents = true;

private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
    Files = Directory.GetFiles(WatchPath, "convergenceUp*.out").OrderBy(x => x).ToList(); // Update List of all files in the directory
    ReadFiles(); // Do fancy stuff with the files
}

这可以按预期工作。每次在监视目录中更改与模式匹配的文件(Notepad ++确实通知我文件已更改),文件将被处理。

从这个简单的“所有文件都在一个目录中”场景开始,我开始为不同类型的文件构建一个观察者(对于那些熟悉OpenFOAM的人来说,强制函数对象)。这些文件保存在分层文件夹结构中,如下所示:

NameOfFunctionObject
|_StartTimeOfSolutionSetup#1
| |_forces.dat
|_StartTimeOfSolutionSetup#2
  |_forces.dat

我的目标是从“NameOfFunctionObject”读取所有的force.dat,并对所有包含的数据做一些诡计。此外,我还希望有机会阅读和观看一个文件。所以我的实现(从上面大量借鉴)目前看起来像这样:

FileSystemWatcher watcher;
if (isSingleFile)
    watcher = new FileSystemWatcher(Directory.GetParent(WatchPath).ToString(), Path.GetFileName(WatchPath));
else
    watcher = new FileSystemWatcher(WatchPath, "forces.dat");
watcher.IncludeSubdirectories = !isSingleFile;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Attributes | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.DirectoryName | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.Security;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Created;
watcher.Deleted += Watcher_Deleted;
watcher.Error += Watcher_Error;
watcher.Renamed += Watcher_Renamed;
watcher.EnableRaisingEvents = isWatchEnabled;

因此,我只想看一个文件或多个文件,我设置了要监视的目录和文件过滤器。如果我观看多个文件,我会将观察者设置为观看子目录。由于进行了严格的测试,我会过滤所有通知并捕获所有观察者事件。

如果我测试单个文件选项,一切都按预期工作,报告并正确处理对文件的更改(同样,使用可信赖的旧Notepad ++进行检查) 在测试多文件选项时,事情变得梨形。 文件路径正确,初始读取按预期工作。但观察者事件都没有发生。好奇的一点是:Notepad ++仍在哔哔声,说文件已经改变,Windows资源管理器显示一个新的文件日期和一个新的文件大小。如果我在Notepad ++中保存文件,则会触发观察者。如果我创建一个匹配模式的新文件,则在监视目录(顶层或以下无关紧要!)后,观察者将被触发。即使正在查看的过滤器来捕获临时文件的创建也不会触发,因此可以安全地假设没有创建临时文件。

通常,观察者的行为符合预期,它可以检测单个文件的更改,它可以检测根目录文件夹及其子文件夹中文件的创建。一旦文件位于子文件夹中,它就无法识别文件的非窗口更改。这种行为是设计的吗?更重要的是:如何在不使用定时器和手动轮询的情况下优雅地工作?

1 个答案:

答案 0 :(得分:2)

我认为这可能与你有关

FileSystemWatcher使用ReadDirectoryChangesW Winapi 调用一些相关的标记

  

当您第一次呼叫ReadDirectoryChangesW时,系统会分配一个   缓冲区来存储更改信息。此缓冲区与   目录句柄,直到它关闭,其大小不会改变   在其一生中。调用之间发生的目录更改   此函数被添加到缓冲区,然后返回下一个   呼叫。 如果缓冲区溢出,则缓冲区的全部内容都是   丢弃

FileSystemWatcher中的模拟是FileSystemWatcher.InternalBufferSize属性

  

备注您可以将缓冲区设置为4 KB或更大,但不能   超过64 KB。如果您尝试将InternalBufferSize属性设置为   小于4096字节,你的值被丢弃了   InternalBufferSize属性设置为4096字节。为了最好   性能,在基于Intel的计算机上使用4 KB的倍数。

     

系统通知组件文件更改,并存储这些更改   组件创建的缓冲区中的更改并传递给API。的每个   事件最多可以使用16个字节的内存,不包括文件名。   如果短时间内有很多变化,缓冲区可能会溢出。   这会导致组件无法跟踪目录中的更改,   它只会提供全面通知。增加的大小   缓冲区可以防止丢失文件系统更改事件。然而,   增加缓冲区大小是昂贵的,因为它来自非分页   无法换出到磁盘的内存,因此请将缓冲区保持为小   尽可能。 要避免缓冲区溢出,请使用NotifyFilter和   IncludeSubdirectories属性可过滤掉不需要的更改   通知

如果情况变得更糟,你可以使用混合的轮询和跟踪,它帮助我摆脱了几次麻烦