Runtime: FileSystemWatcher does not raise events for files that are opened/changed in Visual Studio

Created on 16 Jun 2016  路  4Comments  路  Source: dotnet/runtime

Summary

FileSystemWatcher does not raise events for files that are opened/changed in Visual Studio. It works as expected when using another editor like notepad.

Context

Visual Studio 2015
Visual Studio 15 Preview 2
.NET 4.6.1

Steps to reproduce

  1. Create a FileSystemWatcher that watches files (in common application data folder) like this:

    var configFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ForceFeedbackProgramming");
    
    if (!Directory.Exists(configFolderPath))
      Directory.CreateDirectory(configFolderPath);
    
    var watcher = new FileSystemWatcher()
    {
      Path = configFolderPath,
      NotifyFilter = NotifyFilters.LastWrite,
      Filter = "Config.json"
    };
    
    watcher.Changed += OnFileChanged;
    watcher.EnableRaisingEvents = true;
    
    return watcher;
    
  2. Create a file named "Config.json" in the watched folder (in this example: "CommonApplicationData\ForceFeedbackProgramming").
  3. Run the code from above and set a breakpoint in the OnFileChanged event handler.
  4. Open the Config.json file in Visual Studio, edit it and save it.

    Current Behavior

The event handler is not called and you'll never hit the breakpoint. So I guess, the event is not raised. When checking the Config.json file, the file changed date was set correctly. When viewing the file in another text editor, all changes that have been made in Visual Studio are there. So the file was saved correctly by Visual Studio. So I would assume that the FileSystemWatcher would raise the appropriate events. But it does not. Editing the file in another editor like notepad causes the FileSystemWatcher to raise the events. So the code above works as expected.

Expected Behavior

I would expect that events are raised by the FileSystemWatcher when the watched file(s) are changed and saved in Visual Studio.

Most helpful comment

This isn't a bug in FileSystemWatcher... Visual Studio just isn't doing what you think it's doing. Try changing your code to instead look like:

``` C#
using System;
using System.IO;

class Program
{
static void Main()
{
var configFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ForceFeedbackProgramming");

    if (!Directory.Exists(configFolderPath))
        Directory.CreateDirectory(configFolderPath);

    var watcher = new FileSystemWatcher()
    {
        Path = configFolderPath,
        NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
    };

    watcher.Created += (s, e) => Console.WriteLine("Created: " + e.FullPath);
    watcher.Deleted += (s, e) => Console.WriteLine("Deleted: " + e.FullPath);
    watcher.Changed += (s, e) => Console.WriteLine("Changed: " + e.FullPath);
    watcher.Renamed += (s, e) => Console.WriteLine("Renamed: " + e.OldFullPath + " to " + e.FullPath);
    watcher.EnableRaisingEvents = true;

    Console.ReadLine();
}

}

then edit/save the file in Visual Studio... you should see events something like:

Created: ForceFeedbackProgramming\5jny54l2.szi~
Changed: ForceFeedbackProgramming\5jny54l2.szi~
Created: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
Deleted: ForceFeedbackProgramming\Config.json
Changed: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
Renamed: ForceFeedbackProgramming\5jny54l2.szi~ to ForceFeedbackProgramming\Config.json
Deleted: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
```

In other words, Visual Studio is writing to temporary files, deleting the original file, and then moving the temporary to be the new one.

All 4 comments

This isn't a bug in FileSystemWatcher... Visual Studio just isn't doing what you think it's doing. Try changing your code to instead look like:

``` C#
using System;
using System.IO;

class Program
{
static void Main()
{
var configFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ForceFeedbackProgramming");

    if (!Directory.Exists(configFolderPath))
        Directory.CreateDirectory(configFolderPath);

    var watcher = new FileSystemWatcher()
    {
        Path = configFolderPath,
        NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
    };

    watcher.Created += (s, e) => Console.WriteLine("Created: " + e.FullPath);
    watcher.Deleted += (s, e) => Console.WriteLine("Deleted: " + e.FullPath);
    watcher.Changed += (s, e) => Console.WriteLine("Changed: " + e.FullPath);
    watcher.Renamed += (s, e) => Console.WriteLine("Renamed: " + e.OldFullPath + " to " + e.FullPath);
    watcher.EnableRaisingEvents = true;

    Console.ReadLine();
}

}

then edit/save the file in Visual Studio... you should see events something like:

Created: ForceFeedbackProgramming\5jny54l2.szi~
Changed: ForceFeedbackProgramming\5jny54l2.szi~
Created: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
Deleted: ForceFeedbackProgramming\Config.json
Changed: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
Renamed: ForceFeedbackProgramming\5jny54l2.szi~ to ForceFeedbackProgramming\Config.json
Deleted: ForceFeedbackProgramming\Config.json~RF2249be8.TMP
```

In other words, Visual Studio is writing to temporary files, deleting the original file, and then moving the temporary to be the new one.

Many thanks for the explanation. My filter for a specific file made me blind for the changes in the directory.

But I don't know if I like this. We also use temporaries when loading, editing and saving files. But that is under the hood and completely transparent for the user. So maybe other developers get confused like me, too. So wouldn't it be an option, that the FileSystemWatcher raises the Changed event when a file was created and the NotifyFilter has the LastWrite flag set? So we can see the Created event first, followed by the Changed event? From a semantic point of view, it wouldn't be incorrect: file creation means, that there was a write operation, too. The example from above would work.

But that is under the hood and completely transparent for the user

So is FileSystemWatcher: it's a low-level eventing system that simply passes along the events from the OS. The OS is reporting these events, and, in my opinion, applying a heuristic on top that tries to reinterpret them based on a particular series of events would arguably be misleading for various scenarios. I do understand what you're looking for, but if you want such a heuristic, you can layer that on top of the lower-level events supplied by FSW, and you can do so with basically the same fidelity that we would be able to in the FSW implementation itself.

I can live with that in my particular case. I argue from the FSW API point of view and if the API is natural/intuitive enough here. Well, I think that a file creation is a write operation as well, and so I would expect a Changed event, too - that is no heuristic, that is a question of definition. But as you said, if it should simply pass along the OS events, a different behavior or heuristic could be dangerous in a lot of scenarios. Maybe the documentation could be more clear here. :-)

Many thanks for your explanations!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jchannon picture jchannon  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

aggieben picture aggieben  路  3Comments

bencz picture bencz  路  3Comments

jzabroski picture jzabroski  路  3Comments