Coding problem of the day: I have a service (written in C# .Net) that monitors a folder for incoming files, and when a file appears, it needs to process it. At the moment, it is using a
FileSystemWatcher object. I don't know if I'll continue to use that or not (it was written by someone else before I got here), as it doesn't guarantee any sequence of events, nor does it help if files exist before the service is launched. But that's beside the point.
The problem I discovered is, if the file is coming from another computer over a slow link (e.g. an FTP or other slow network transfer), the
FileSystemWatcher will raise its Created event as soon as the file appears, but the file is not yet ready. (I simulated this by writing another program that slowly writes a very large file to the target directory, using a loop and a
The solution, like a lot of other things in programming, is a little convoluted, but it seems to work for the time being. The gist of it is, in the Created event handler, I first call a function that tries to open the file for exclusive read access. If the file is still open, this will fail.
Testing for this failure is the hard part. The exception that gets thrown is a fairly generic
IOException, and while a "file in use" is one condition for which I want to stop and wait, there are other conditions that I would quite definitely not want to wait to magically resolve themselves. The MSDN doc on
IOException lists several derived classes that, for example, I would rather treat as critical errors immediately, like
FileNotFoundException…. If I waited on those, I have a feeling my code would be waiting a very long time.
So, here's my "is this file locked" function:
As I note in the comments, I'm concerned this might not work on other locales, since I'm specifically looking in the exception's message text for the string "another process". Unfortunately, I don't have a better way to determine what
IOException got thrown. I set a breakpoint and tested it, and (at least in .Net 2.0 on Windows XP SP3), it was indeed throwing a base
System.IO.IOException with that text in the message.
I'm open to any better ideas, though….