Subscribe via RSS
5May/150

FileSystemWatcher isn’t watching your files?

Quick note that just stole 2 hours of my weekend; of which I'll never get back... It seems that the FileSystemWatcher in C# it capable of watching files... yes.... but it turns out that the filters aren't as simple as you'd think.

Say you want to watch for new or changed text files in a folder? Filter = "*.txt" right? And the folder has a crapload of *.txt files? that's how it displays in Explorer? Right?

Wrong... Check out this sample code... yes, I'm holding the files open in my own source, but this is just a dirty way to show what's happening:

        static void Main(string[] args) {
            string folder = @"d:\temp\fileWatcherFolder\";
            FileSystemWatcher fsw = new FileSystemWatcher(folder, "*.txt");
            fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess;
            fsw.Changed += fsw_Changed;
            fsw.EnableRaisingEvents = true;
            using (StreamWriter file = new StreamWriter(folder + "test5.txt")) {
                Console.WriteLine("[");
                for (int i = 0; i < 10; i++) {
                    file.WriteLine("test line " + i);
                    Console.WriteLine(".");
                    Thread.Sleep(1000);
                }
                Console.WriteLine("]");
            }
            Console.ReadKey();
        }
        static void fsw_Changed(object sender, FileSystemEventArgs e) {
            Console.WriteLine(e.Name + " was changed!");
        }

From above, you can see that I start a watcher on a known folder, I then start a new file and write to it. The output should always show that updates are occurring. The updates never come... and this is symptomatic of what I had with another external application that was holding files open. It also then presented a bigger issue of adding tildes to the end of the extensions.

It seems that this is an issue in .NET regarding file flushing. The application with the file handles open must flush the data out for a FileSystemWatcher to pick it up. Depending on the speed and quantity of files being flushed, the watcher may have to be customised to cope with the load. Check out the InternalBufferSize parameter.

Note in the above code, you can force a file.Flush() after the file.WriteLine, you'll get a single event. If you use file.FlushAsync() then you'll get ALL of the events! There is no threading in my example code, so the fact that async works makes sense. Of course, if you have no control over the application that is writing the files (as I didn't) then you're stuck trying your hardest to watch the files.

I still can't see my files?!

It gets better though... on some versions of windows, you may will not even see the filename with the expected extension... there may actually be a tilde at the end of the name... invisible to the naked eye. Explorer will show "test5.txt" but the watcher will see "test5.txt~"! It seems that if another application has the file open, the operating system keeps the filename with a tilde at the end until 'flushed' to disk. I haven't been able to reproduce this via the source above, using notepad to hold files open... but I know it happens. The application I had to deal with spewed files out quickly, holding them open, and the watcher only saw "txt~" extensions...

If this is the case, then you can loosen your filter to "*.txt*". Note that if you have a LOT of files being created, then this will slow down the watcher considerably. I actually recommend that you set up a second watcher, one for "*.txt" and the other for "*.txt~".

It's only when you watch the files with a filter of *.* that you'll see the crappy extension names. So when trying to debug this... set your filter as 'relaxed' as possible to capture changes and tighten it when you know what you are looking for.

Be careful! It seems others have had the same problems: FileSystemWatcher class does not fire Change events when NotifyFilters.Size is used.