VB.NET FileSystemWatcher 多个更改事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/449993/
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
VB.NET FileSystemWatcher Multiple Change Events
提问by John Kurlak
I have the following code:
我有以下代码:
Imports System.IO
Public Class Blah
Public Sub New()
InitializeComponent()
Dim watcher As New FileSystemWatcher("C:\")
watcher.EnableRaisingEvents = True
AddHandler watcher.Changed, AddressOf watcher_Changed
End Sub
Private Sub watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs)
MsgBox(e.FullPath)
End Sub
End Class
When I run it and save changes to a file on my C drive, the code works great, except it executes the watcher_Changed() method four times. Any idea why? The changeType is "4" every time.
当我运行它并将更改保存到我的 C 驱动器上的文件时,代码运行良好,除了它执行四次 watcher_Changed() 方法。知道为什么吗?每次更改类型都是“4”。
Thanks.
谢谢。
回答by John Kurlak
From the "Troubleshooting FileSystemWatcher Components" section of the VS.NET documentation...
从 VS.NET 文档的“疑难解答 FileSystemWatcher 组件”部分...
Multiple Created Events Generated for a Single Action
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.
Note: Notepad may also cause other interesting event generations. For example, if you use the ChangeEventFilter to specify that you want to watch only for attribute changes, and then you write to a file in the directory you are watching using Notepad, you will raise an event . This is because Notepad updates the Archived attribute for the file during this operation.
为单个操作生成的多个创建事件
您可能会注意到,在某些情况下,单个创建事件会生成多个由您的组件处理的 Created 事件。例如,如果您使用 FileSystemWatcher 组件监视目录中新文件的创建,然后使用记事本创建文件对其进行测试,您可能会看到生成了两个 Created 事件,即使只创建了一个文件。这是因为记事本在写入过程中会执行多个文件系统操作。记事本批量写入磁盘,创建文件内容和文件属性。其他应用程序可以以相同的方式执行。因为 FileSystemWatcher 监视操作系统活动,所以这些应用程序触发的所有事件都将被拾取。
注意:记事本也可能导致其他有趣的事件生成。例如,如果您使用 ChangeEventFilter 指定您只想监视属性更改,然后您使用记事本写入您正在监视的目录中的文件,则会引发一个 event 。这是因为记事本在此操作期间更新了文件的存档属性。
回答by Frederik Gheysels
A while ago, I've experience the same problem.
不久前,我遇到了同样的问题。
After some searching thtrough the web, it appeared that I was not the only one having this issue. :) So, perhaps it is a flaw in the FileSystemWatcher ...
在网上搜索了一番之后,似乎我不是唯一一个遇到这个问题的人。:) 所以,也许这是 FileSystemWatcher 的一个缺陷......
I've solved it by keeping track of the last time the eventhandler has been raised. If it has been raised less then xxx msec ago, I return from my eventhandler. If anyone knows a fix that is more elegant; plz let me know. :)
我已经通过跟踪事件处理程序上次引发的时间来解决它。如果它在 xxx 毫秒前被引发,我从我的事件处理程序返回。如果有人知道更优雅的修复;请告诉我。:)
This is how I've worked around it:
这就是我解决它的方式:
if( e.ChangeType == WatcherChangeTypes.Changed )
{
// There is a nasty bug in the FileSystemWatch which causes the
// events of the FileSystemWatcher to be called twice.
// There are a lot of resources about this to be found on the Internet,
// but there are no real solutions.
// Therefore, this workaround is necessary:
// If the last time that the event has been raised is only a few msec away,
// we ignore it.
if( DateTime.Now.Subtract (_lastTimeFileWatcherEventRaised).TotalMilliseconds < 500 )
{
return;
}
_lastTimeFileWatcherEventRaised = DateTime.Now;
.. handle event
回答by LOAS
My solution to this problem is a bit like Erics except I use a System.Windows.Forms.Timer in stead of starting a new thread. The idea is that I handle the change event only when x ms have passed without any file changed events. Note that everything takes place on the GUI thread so there are no threading issues. I use x = 100.
我对这个问题的解决方案有点像 Eric,只是我使用 System.Windows.Forms.Timer 而不是启动一个新线程。这个想法是我只在 x ms 过去而没有任何文件更改事件时处理更改事件。请注意,一切都发生在 GUI 线程上,因此不存在线程问题。我使用 x = 100。
private Dictionary<String, FileSystemEventArgs> xmlFileChangedEvents = new Dictionary<string, FileSystemEventArgs>();
private void debugXmlWatcher_Changed(object sender, FileSystemEventArgs e)
{
if (!xmlFileChangedEvents.ContainsKey(e.Name))
xmlFileChangedEvents.Add(e.Name, e);
xmlChangeTimer.Stop();//Reset the Forms.Timer so that it times out in 100 ms
xmlChangeTimer.Start();
}
private void xmlChangeTimer_Tick(object sender, EventArgs e)
{
foreach (FileSystemEventArgs eventArg in xmlFileChangedEvents.Values)
{
//
//Handle the file changed event here
//
}
xmlFileChangedEvents.Clear();
}
回答by sam
the watcher changed event handler will fire on 3 events... create, delete,change. Only when you rename a file will the onrenamed event fire. That is probably why you are getting 4 alerts. Also most programs run multiple operations on a file before closing it. Every event is considered a change and so the on_changed event is fired every time.
观察者更改的事件处理程序将触发 3 个事件...创建、删除、更改。只有当您重命名文件时,才会触发 onrenamed 事件。这可能就是您收到 4 个警报的原因。此外,大多数程序在关闭文件之前会对文件运行多个操作。每个事件都被认为是一个更改,因此每次都会触发 on_changed 事件。
回答by John
If you need to display the change events while they are happening on a form, then you need to use threading. Eric's solution is the best in this regard since it can be easily used with or without a form making the solution most flexible. It also handles the multiple duplicate events nicely and makes sure that it only eats duplicate events only if it is for THE SAME FILE. In the accepted solution, if two files are changed at near the same time, one of their events could be incorrectly ignored.
如果您需要在表单上发生更改事件时显示它们,那么您需要使用线程。Eric 的解决方案在这方面是最好的,因为它可以在有或没有表单的情况下轻松使用,使解决方案最灵活。它还可以很好地处理多个重复事件,并确保仅当它用于相同文件时才处理重复事件。在公认的解决方案中,如果两个文件几乎同时更改,则其中一个事件可能会被错误地忽略。
回答by Justin Emlay
Frederik's solution is by the far the best thing I've come across. However I found 500 milliseconds to be far too slow. In my app a user is able to perform two actions on a file easily within .5 seconds so I lowered it to 100 and so far it's working out fine. His C# was a little fubar (it wouldn't convert) so here's the VB version:
Frederik 的解决方案是迄今为止我遇到的最好的解决方案。但是我发现 500 毫秒太慢了。在我的应用程序中,用户能够在 0.5 秒内轻松对文件执行两个操作,因此我将其降低到 100,到目前为止效果很好。他的 C# 有点笨拙(它不会转换)所以这是 VB 版本:
Public LastTimeFileWatcherEventRaised As DateTime
If DateTime.Now.Subtract(LastTimeFileWatcherEventRaised).TotalMilliseconds < 100 Then Return
LastTimeFileWatcherEventRaised = DateTime.Now
.. handle event here
回答by Fedor
I made a simple class that works fine for me. It may be useful for someone else.
我做了一个简单的类,对我来说很好用。它可能对其他人有用。
using System;
using System.IO;
using System.Timers;
namespace Demo
{
class FileWatcher
{
private FileSystemWatcher watcher = new FileSystemWatcher();
private Timer t = new Timer();
public event EventHandler FileChanged;
public FileWatcher()
{
t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
t.Interval = 1000;
}
public void Start(String path)
{
watcher.Path = Path.GetDirectoryName(path);
watcher.Filter = Path.GetFileName(path);
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime;
watcher.EnableRaisingEvents = true;
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
}
void watcher_Changed(object sender, FileSystemEventArgs e)
{
if (!t.Enabled)
t.Start();
}
void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
t.Stop();
if (FileChanged != null)
FileChanged(this, null);
}
}
}
Can be used like that:
可以这样使用:
FileWatcher FileWatcher1 = new FileWatcher();
FileWatcher1.FileChanged += new EventHandler(FileWatcher1_FileChanged);
FileWatcher1.Start("c:\test.txt");
回答by dlchambers
This has been a maddening quirk of the FindFirstChangeNotification() Win32 API since day 1 (since Windows 3.x), and it looks like FileSystemWatcher simply wraps that API. The timer approach (presented above) is the common workaround.
这是 FindFirstChangeNotification() Win32 API 从第一天起(从 Windows 3.x 开始)的一个令人抓狂的怪癖,看起来 FileSystemWatcher 只是简单地包装了该 API。计时器方法(如上所示)是常见的解决方法。
I usually create a class that wraps FileSystemWatcher and does the multiple-change-call filtering. A bit of extra work to write, but it pays off in reuse.
我通常创建一个类来包装 FileSystemWatcher 并执行多次更改调用过滤。需要编写一些额外的工作,但重用是值得的。
public class FileChangeMonitor
{
private FileSystemWatcher _fsw;
DateTime _lastEventTime;
public event FileSystemEventHandler Changed;
public FileChangeMonitor(string path, string filter)
{
_fsw = new FileSystemWatcher(path, filter);
_fsw.Changed += new FileSystemEventHandler(_fsw_Changed);
_fsw.EnableRaisingEvents = true;
_fsw.NotifyFilter = NotifyFilters.LastWrite;
_fsw.IncludeSubdirectories = false;
}
private void _fsw_Changed(object sender, FileSystemEventArgs e)
{
// Fix the FindFirstChangeNotification() double-call bug
if (DateTime.Now.Subtract(_lastEventTime).TotalMilliseconds > 100)
{
_lastEventTime = DateTime.Now;
if (this.Changed != null)
this.Changed(sender, e); // Bubble the event
}
}
}
You can then use FileChangeMonitor pretty much like you would FileSystemWatcher:
然后,您可以像使用 FileSystemWatcher 一样使用 FileChangeMonitor:
FileChangeMonitor fcm = new FileChangeMonitor(path, filter);
fsm.Changed += new FileSystemEventHandler(fsm_Changed);
...
Of course, the code above only handles the Changed event and NotifyFilters.LastWrite, but you get the idea.
当然,上面的代码只处理了 Changed 事件和 NotifyFilters.LastWrite,但你明白了。
回答by Xaqron
Platform independent trick :
平台独立技巧:
// Class level variable
bool m_FileSystemWatcherIsMessy = true;
// inside call back
if (m_FileSystemWatcherIsMessy) {
m_FileSystemWatcherIsMessy = false;
return;
} else {
m_FileSystemWatcherIsMessy = true;
}
回答by lc.
Assuming the path is the same every time, is it possible the program you are using to save the file is actually doing the save in pieces? Or do you have more than one Blahinstantiated?
假设路径每次都相同,是否有可能您用来保存文件的程序实际上是分段保存的?或者你有不止一个Blah实例化?
Edit: Do you have any antivirus auto-protect software running? Those might be touching the file in the process.
编辑:您是否正在运行任何防病毒自动保护软件?那些人可能会在此过程中接触文件。
From the MSDN Documentation:
从MSDN 文档:
Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
常见的文件系统操作可能会引发多个事件。例如,当文件从一个目录移动到另一个目录时,可能会引发多个 OnChanged 以及一些 OnCreated 和 OnDeleted 事件。移动文件是一个复杂的操作,由多个简单的操作组成,因此会引发多个事件。同样,某些应用程序(例如,防病毒软件)可能会导致 FileSystemWatcher 检测到的其他文件系统事件。
Edit: Or maybe there's something to do with how windows is saving the file. You might be getting more than one event from different changes. (One for the size, one for the last write timestamp, one for the last access timestamp, and one more for...something else.) Try setting the FileSystemWatcher's NotifyFilterproperty to a single type of change and see if you continue to get multiple events.
编辑:或者可能与 Windows 保存文件的方式有关。您可能会从不同的更改中获得多个事件。(一个用于大小,一个用于最后一次写入时间戳,一个用于最后访问时间戳,还有一个用于...其他东西。)尝试将FileSystemWatcher'sNotifyFilter属性设置为单一类型的更改,看看是否继续获得多个事件。

