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.
Thanks.
From the "Troubleshooting FileSystemWatcher Components" section of the VS.NET documentation...
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.
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 ...
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. :)
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
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.
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();
}
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.
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:
Public LastTimeFileWatcherEventRaised As DateTime
If DateTime.Now.Subtract(LastTimeFileWatcherEventRaised).TotalMilliseconds < 100 Then Return
LastTimeFileWatcherEventRaised = DateTime.Now
.. handle event here
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 Blah
instantiated?
Edit: Do you have any antivirus auto-protect software running? Those might be touching the file in the process.
From the MSDN Documentation:
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.
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 NotifyFilter
property to a single type of change and see if you continue to get multiple events.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With