4

[Using C# and Windows as platform]

I have a camera that writes JPG files to a local folder in my PC. I want to load each file the camera drops, so I have a FileSystemWatcher that notifies me whenever a new picture is created, but the camera locks the file while it's being written, so if I try to load it just after being notified of its creation, I get an exception saying that the file is locked.

Currently, I have a while loop (with a Thread.Sleep) that retries to load the image every 0.2 seconds, but it feels a bit dirty.

Is there a more elegant way to wait until the lock has been released, so I can load the file being sure that it's no longer used??

SuperJMN
  • 13,110
  • 16
  • 86
  • 185
  • Possible duplicate of [How to check for file lock?](http://stackoverflow.com/questions/1304/how-to-check-for-file-lock) – Ňɏssa Pøngjǣrdenlarp Mar 24 '16 at 16:43
  • 1
    I don't want to know if it's locked. I want to be notified when it's NOT locked. – SuperJMN Mar 24 '16 at 16:46
  • 2
    have a look [https://social.msdn.microsoft.com/Forums/vstudio/en-US/dd1d86a8-b803-4425-bf84-db9a4b7c1d52/filesystemwatcher-for-locks-readwrite-lock-on-files?forum=csharpgeneral](https://social.msdn.microsoft.com/Forums/vstudio/en-US/dd1d86a8-b803-4425-bf84-db9a4b7c1d52/filesystemwatcher-for-locks-readwrite-lock-on-files?forum=csharpgeneral) – esiprogrammer Mar 24 '16 at 16:48
  • 1
    The accepted answer in the question that Plutonix referenced suggests a good thing to consider: even if you know that the file has been released, potentially it can get locked again before you open it. So the robust way would be to try opening the file in a loop after getting a notification about lock release. This is just a comment. – Eugene Mayevski 'Callback Mar 24 '16 at 16:56

1 Answers1

2

You will not be able to get around trial and error approach, i.e., try to open the file, catch IOException, try again. However, you can hide this ugliness in a separate class like so:

public class CustomWatcher
{
    private readonly FileSystemWatcher watcher;
    public event EventHandler<FileSystemEventArgs> CreatedAndReleased;

    public CustomWatcher(string path)
    {
        watcher = new FileSystemWatcher(path, "*.jpg");
        watcher.Created += OnFileCreated;
        watcher.EnableRaisingEvents = true;
    }

    private void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        // Running the loop on another thread. That means the event
        // callback will be on the new thread. This can be omitted
        // if it does not matter if you are blocking the current thread.
        Task.Run(() =>
        {
            // Obviously some sort of timeout could be useful here.
            // Test until you can open the file, then trigger the CreeatedAndReleased event.
            while (!CanOpen(e.FullPath))
            {
                Thread.Sleep(200);
            }
            OnCreatedAndReleased(e);
        });
    }

    private void OnCreatedAndReleased(FileSystemEventArgs e)
    {
        CreatedAndReleased?.Invoke(this, e);
    }

    private static bool CanOpen(string file)
    {
        FileStream stream = null;
        try
        {
            stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (IOException)
        {
            return false;
        }
        finally
        {
            stream?.Close();
        }
        return true;
    }
}

This "watcher" can be used like this:

var watcher = new CustomWatcher("path");
watcher.CreatedAndReleased += (o,e) => 
{
    // Now, your watcher has managed to open and close the file,
    // so the camera is done with it. Obviously, any other application
    // is able to lock it before this code manages to open the file.
    var stream = File.OpenRead(e.FullPath);
}

Disclaimer: CustomWatcher probably needs to be IDisposable and dispose of the FileSystemWatcher appropriately. The code merely shows an example of how to achieve the desired functionality.

lenkan
  • 4,089
  • 1
  • 14
  • 12