FileSystemWatcher 对子目录中的更改不可靠
FileSystemWatcher unreliable for changes in subdirectory
我目前正在为 OpenFOAM 输出文件实施文件内容观察器。这些文件由 OpenFOAM 在 Unix 环境中编写,并由我的应用程序在 Windows 环境中使用。
请考虑我的第一个 working 收敛文件观察者(这些文件在解决方案的每次迭代后得到更新):
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++ 确实通知我文件也已更改),文件都会被处理。
从这个简单的 "all files are in one directory" 场景开始,我开始为不同类型的文件构建一个观察器(对于那些熟悉 OpenFOAM 的人来说是 Force 函数对象)。这些文件保存在分层文件夹结构中,如下所示:
NameOfFunctionObject
|_StartTimeOfSolutionSetup#1
| |_forces.dat
|_StartTimeOfSolutionSetup#2
|_forces.dat
我的目标是从 "NameOfFunctionObject" 中读取所有 forces.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++ 中,则会触发观察器。如果我在监视目录中创建一个与模式匹配的新文件(顶级或更低级别无关紧要!),则会触发监视程序。即使监视过滤器 . 以捕获临时文件的创建也不会触发,因此可以安全地假设没有创建临时文件。
一般来说,watcher 的行为符合预期,它可以检测到对单个文件的更改,它可以检测根监视文件夹及其子文件夹中文件的创建。一旦文件位于子文件夹中,它就无法识别对文件的非 windows 更改。这是设计使然的行为吗?更重要的是:如何在不使用计时器和手动轮询的情况下优雅地解决它?
我认为这可能与您有关
FileSystemWatcher
使用 ReadDirectoryChangesW
Winapi 调用和一些相关标志
When you first call ReadDirectoryChangesW
, the system allocates a
buffer to store change information. This buffer is associated with the
directory handle until it is closed and its size does not change
during its lifetime. Directory changes that occur between calls to
this function are added to the buffer and then returned with the next
call. If the buffer overflows, the entire contents of the buffer are
discarded
FileSystemWatcher
中的类似物是 FileSystemWatcher.InternalBufferSize
属性
Remarks You can set the buffer to 4 KB or larger, but it must not
exceed 64 KB. If you try to set the InternalBufferSize property to
less than 4096 bytes, your value is discarded and the
InternalBufferSize property is set to 4096 bytes. For best
performance, use a multiple of 4 KB on Intel-based computers.
The system notifies the component of file changes, and it stores those
changes in a buffer the component creates and passes to the APIs. Each
event can use up to 16 bytes of memory, not including the file name.
If there are many changes in a short time, the buffer can overflow.
This causes the component to lose track of changes in the directory,
and it will only provide blanket notification. Increasing the size of
the buffer can prevent missing file system change events. However,
increasing buffer size is expensive, because it comes from non-paged
memory that cannot be swapped out to disk, so keep the buffer as small
as possible. To avoid a buffer overflow, use the NotifyFilter and
IncludeSubdirectories properties to filter out unwanted change
notifications.
如果情况变得更糟,您可以结合使用轮询和跟踪,它帮助我摆脱了几次麻烦
我目前正在为 OpenFOAM 输出文件实施文件内容观察器。这些文件由 OpenFOAM 在 Unix 环境中编写,并由我的应用程序在 Windows 环境中使用。
请考虑我的第一个 working 收敛文件观察者(这些文件在解决方案的每次迭代后得到更新):
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++ 确实通知我文件也已更改),文件都会被处理。
从这个简单的 "all files are in one directory" 场景开始,我开始为不同类型的文件构建一个观察器(对于那些熟悉 OpenFOAM 的人来说是 Force 函数对象)。这些文件保存在分层文件夹结构中,如下所示:
NameOfFunctionObject
|_StartTimeOfSolutionSetup#1
| |_forces.dat
|_StartTimeOfSolutionSetup#2
|_forces.dat
我的目标是从 "NameOfFunctionObject" 中读取所有 forces.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++ 中,则会触发观察器。如果我在监视目录中创建一个与模式匹配的新文件(顶级或更低级别无关紧要!),则会触发监视程序。即使监视过滤器 . 以捕获临时文件的创建也不会触发,因此可以安全地假设没有创建临时文件。
一般来说,watcher 的行为符合预期,它可以检测到对单个文件的更改,它可以检测根监视文件夹及其子文件夹中文件的创建。一旦文件位于子文件夹中,它就无法识别对文件的非 windows 更改。这是设计使然的行为吗?更重要的是:如何在不使用计时器和手动轮询的情况下优雅地解决它?
我认为这可能与您有关
FileSystemWatcher
使用 ReadDirectoryChangesW
Winapi 调用和一些相关标志
When you first call
ReadDirectoryChangesW
, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded
FileSystemWatcher
中的类似物是 FileSystemWatcher.InternalBufferSize
属性
Remarks You can set the buffer to 4 KB or larger, but it must not exceed 64 KB. If you try to set the InternalBufferSize property to less than 4096 bytes, your value is discarded and the InternalBufferSize property is set to 4096 bytes. For best performance, use a multiple of 4 KB on Intel-based computers.
The system notifies the component of file changes, and it stores those changes in a buffer the component creates and passes to the APIs. Each event can use up to 16 bytes of memory, not including the file name. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer can prevent missing file system change events. However, increasing buffer size is expensive, because it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small as possible. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties to filter out unwanted change notifications.
如果情况变得更糟,您可以结合使用轮询和跟踪,它帮助我摆脱了几次麻烦