views:

1648

answers:

8

I have an application where I am looking for a text file and if there are any changes made to the file I am using the onchanged eventhandler to handle the event. I am using the notifyfilters.Lastwritetime but still the event is getting fired twice.Here is the code.

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

I my case the Onchanged is called twice when I change the text file version.txt and save it.Please help.

+22  A: 

I am afraid that this is a well-known bug/feature of the FileSystemWatcher class. This is from the documentation of the class:

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.

Now this bit of text is about the Created event, but the same thing applies to other file events as well. In some applications you might be able to get around this by using the NotifyFilter property, but my experience is says that sometimes you have to do some manual duplicate filtering (hacks) as well.

A while ago I bookedmarked a page with a few FileSystemWatcher tips. You might want to check it out.

Jørn Schou-Rode
Nice, thanks for posting the tips. Good stuff. +1 from me.
Wil P
+2  A: 

I have changed the way I monitor files in directories. Instead of using the FileSystemWatcher I poll locations on another thread and then look at the LastWriteTime of the file.

DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);

Using this information and keeping an index of a file path and it's latest write time I can determine files that have changed or that have been created in a particular location. This removes me from the oddities of the FileSystemWatcher. The main downside is that you need a data structure to store the LastWriteTime and the reference to the file, but it is reliable and easy to implement.

Wil P
as well as you have to burn background cycles instead of being notified by a system event.
Matthew Whited
A: 

You could try to open it for write, and if successful then you could assume the other application is done with the file.

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

Just opening it for write appears not to raise the changed event. So it should be safe.

MatteKarla
+1  A: 

I've "fixed" that problem using the following strategy in my delegate:

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}
David Brabant
+1  A: 

Any duplicated OnChanged events from the FileSystemWatcher can be detected and discarded by checking the File.GetLastWriteTime timestamp on the file in question. Like so:

    DateTime lastRead = DateTime.MinValue;

    void OnChanged(object source, FileSystemEventArgs a)
    {
        DateTime lastWriteTime = File.GetLastWriteTime(uri);
        if (lastWriteTime != lastRead)
        {
            doStuff();
            lastRead = lastWriteTime;
        }
        // else discard the (duplicated) OnChanged event
    }
BaBu
A: 

if you register to the OnChanged event, then by deleting the monitored file before changing it might work, as long as you only need to monitor the OnChange event..

guy yogev
A: 

Try this:

private void OnChanged(object source, FileSystemEventArgs e)
{
   FileInfo fInfo = new FileInfo(e.FullPath);
    try
    { 
        // do your stuff
        Console.WriteLine("\nChanged: \n" + e.FullPath + " " + fInfo.Length);    

    }
    catch
    { 
        // nothing
    }
}
Gordon Dura
A: 

My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.

Ikon