Pulling the rug from under Uncle FileSystemWatcher

The FileSystemWatcher class is a nice treat if you need to trace file system modifications (e.g. wait for a file to be created or deleted in a specific path).

The full MSDN documentation gives specs and usage examples.

You will see it's easy to use and still powerful, allowing a variety of scenarios to be monitored. There is one point, though, that I had to painfully find out by messing it up before realizing it was an issue at all.

Imagine the following scenario:
- You are monitoring a network path (UNC or mapped drive), or let's rather say your FileSystemWatcher is.
- The network path becomes unavailable (deleted or unavailable due to network trouble...)

What would you expect? Well, forget about that, because what actually happens is the following: Nothing. Period.Even if the path resurfaces, your FileSystemWatcher is out of its game. That renders it rather useless, because who has a use for a deaf event listener?

Anyway, that's what recently happened within a windows service I've developed. The solution is as plain as good:
FileSystemWatcher offers an Error-event that will be fired in such cases. Subscribe to this event and, in the handler, create a new FileSystemWatcher to listen for events on the original Watcher's path.
Check out the following program, which will watch a path C:\test on your local machine. Once it's running, delete the folder. If you re-create it within 30 seconds (well, 27 really) watching will be resumed, otherwise not.


class Program
{
private static FileSystemWatcher fsw;

private static int retryInterval = 3;

private static int maxRetries = 10;

private static int numRetries = 0;

private static bool watcherInstanceCompromised;

[STAThread]
static void Main(string[] args)
{
try
{
Init();

Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message, e);
throw;
}
}

private static void Init()
{
try
{
fsw = new FileSystemWatcher();
fsw.Path = @"\\127.0.0.1\c$\test";
fsw.Created += OnCreated;
fsw.Error += OnError;
fsw.EnableRaisingEvents = true;
Console.WriteLine("watching {0}", fsw.Path);
numRetries = 0;
watcherInstanceCompromised = false;
}
catch (Exception e)
{
numRetries++;
Console.WriteLine( "could not initialize ({0})", numRetries );
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}

private static void OnCreated(object source, FileSystemEventArgs f)
{
Console.WriteLine( "file created : {0}", f.FullPath );
}

private static void OnError(object source, ErrorEventArgs e)
{
watcherInstanceCompromised = true;
Console.WriteLine( "error occured." );
fsw.Dispose();
while (numRetries < maxRetries && watcherInstanceCompromised)
{
Console.WriteLine("trying to init after {0} failed attempts", numRetries);
Init();
// sleep for a few seconds
System.Threading.Thread.Sleep(1000 * retryInterval);
}
if (watcherInstanceCompromised)
{
Console.WriteLine( "not back after {0} retries.", numRetries );
}
else
{
Console.WriteLine( "should be working smoothly now" );
}
return;
}
}

Kommentare

Beliebte Posts aus diesem Blog

WPF Diaries : Using HLSL pixel shaders

Getting the Crisp back into your WPF Apps

WPF and Workflow Foundation : XAML parser compatibility issues