Every time I am asked to make a program involving “Watching” for files that are written to a disk I cringe. Thoughts of missing files, multiple events being raised and endless frustration jump into my head. My latest bout with the .NET FileSystemWatcher lead to me to yet another annoyance with the FileSystemWatcher.
Like most coders I test my code locally and try to break it with random scenarios. I also jump on some virtual machines to test it on a non-development machine. I then send it to QA. The latest app I created passed all my tests but would randomly crash here and there. It would write an error to the event log on the test machine that read:

‘EventType clr20r3, P1 [myexenamehere].exe, P2 1.0.0.0, P3 48a3148c, P4 mscorlib, P5 2.0.0.0, P6 4333ab80, P7 32f8, P8 21c, P9 system.io.ioexception, P10 NIL.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.’

Awesome. So I know there is a problem with the application and doing something with a file. To further investigate, I implemented a global exception handler on the app to get some good information. To do this, go to: http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx and if that link is broken and you are now upset, search microsoft.com for “AppDomain UnhandledException Event” that should bring to where they moved it.
After implementing that little trick in my code like so:


[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(CatchAllExceptionHandler);

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}

static void CatchAllExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception ex = (Exception)args.ExceptionObject;
//Dump Exception
string name = string.Format("DUMP-{0}.txt", Guid.NewGuid());

using (StreamWriter sw = File.AppendText(name))
{
sw.WriteLine(ex.Message);
sw.Write(ex.Source);
sw.Write(ex.StackTrace);
sw.Close();
}
}

I was able to have QA run the application and get a nice dump with the actual exception message and stacktrace. Here is what it said:

‘The process cannot access the file ‘C:\FILE\TEST.TXT’ because it is being used by another process.
[STACK TRACE WAS HERE, It was on a File.Copy command]’

With that I was able to find the problem. The issue was when a file was changed the file watcher reported that to me and in the event for that I had to move the file to another directory. The files were about 1k in size, no big whoop. Turns out when the file changes the FileSystemWatcher raises the event right away I then was copying a file that was still being copied by the OS resulting in that message. So, no big deal right? I’ll just simply check to make sure the file is not in use by using the “IsInUse” property right? Heck no, that doesn’t exist. So what does exist you ask? From what I could find “Programming relying on exceptions” Which is >:XX:crazy:. However, due to time contstraints I came up with this crazy thing:


private void SafeFileCopy(string source, string destination)
{
try
{
File.Copy(source, destination);
}
catch (IOException ex)
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " - Inuse... pausing!");
System.Threading.Thread.Sleep(2000);
SafeFileCopy(source, destination);
}
}

Yeah, this has it all. Large over head for exception handling, possibility for infinite recursion… how badly do you just want to put that into your code and use it…. exactly. Anyway, that is where I am at now in identifying another annoyance with the costly and unreliability of the FileSystemWatcher. I am now looking into ways of asking the OS “Hey, you using this?” as opposed to this but I have not done that before so I’ll get back to you on that.

Leave a Reply

Your email address will not be published. Required fields are marked *