What's the simplest way of blocking a thread until a file has been unlocked and is accessible for reading and renaming? For example, is there a WaitOnFile() somewhere in the .NET Framework?
I have a service that uses a FileSystemWatcher to look for files that are to be transmitted to an FTP site, but the file created event fires before the other process has finished writing the file.
The ideal solution would have a timeout period so the thread doesn't hang forever before giving up.
Edit: After trying out some of the solutions below, I ended up changing the system so that all files wrote to Path.GetTempFileName()
, then performed a File.Move()
to the final location. As soon as the FileSystemWatcher
event fired, the file was already complete.
I don't know what you're using to determine the file's lock status, but something like this should do it.
Starting from Eric's answer, I included some improvements to make the code far more compact and reusable. Hope it's useful.
Simply use the Changed event with the NotifyFilter NotifyFilters.LastWrite:
Here is a generic code to do this, independant from the file operation itself. This is an example on how to use it:
or
You can also define the retry count, and the wait time between retries.
NOTE: Unfortunately, the underlying Win32 error (ERROR_SHARING_VIOLATION) is not exposed with .NET, so I have added a small hack function (
IsSharingViolation
) based on reflection mechanisms to check this.A possible solution would be, to combine a filesystemwatcher with some polling,
get Notified for every Change on a File, and when getting notified check if it is locked as stated in the currently accepted answer: https://stackoverflow.com/a/50800/6754146 The code for opening the filestream is copied from the answer and slightly modified:
With this way you can Check for a file if its locked and get notified when its closed over the specified callback, this way you avoid the overly aggressive polling and only do the work when it may be actually be closed
From MSDN:
Your FileSystemWatcher could be modified so that it doesn't do its read/rename during the "OnCreated" event, but rather: