tags:

views:

10797

answers:

6

I'm writihg a program in C# that needs to repeatedly access 1 image file. Most of the time it works, but if my computer's running fast, it will try to access the file before it's been saved back to the filesystem and throw an error "File in use by another process".

I would like to find a way around this, but all my Googling has only yielded creating checks by using exception handling. This is against my religion, so I was wondering if anyone has a better way of doing it?

Thanks!

+1  A: 

the only way I know of is to use the Win32 exclusive lock API which isn't too speedy, but examples exist.

Most people, for a simple solution to this, simply to try/catch/sleep loops.

Luke Schafer
+3  A: 

Perhaps you could use a FileSystemWatcher and watch for the Changed event.

I haven't used this myself, but it might be worth a shot. If the filesystemwatcher turns out to be a bit heavy for this case, I would go for the try/catch/sleep loop.

Charlie boy
+11  A: 

You can suffer from a thread race condition on this which there are documented examples of this being used as a security vulnerability. If you check that the file is available, but then try and use it you could throw at that point, which a malicious user could use to force and exploit in your code.

Your best bet is a try catch / finally which trys to get the file handle.

try
{
   using (Stream stream = new FileStream("MyFilename.txt"))
   {
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}
Spence
+1. This is the correct solution.
ShellShock
+1  A: 

Try and move/copy the file to a temp dir. If you can, it has no lock and you can safely work in the temp dir without getting locks. Else just try to move it again in x seconds.

Carra
A: 

Hi Dawsy,

I had a similar problem and did something that seemed to work, it was using exception handling though...

I put a counter in to keep trying 100 times to stop endless loop.

See below...

    private void uploadFiles(string filename)
    {
        try
        {
            string fromFileAndPath = Properties.Settings.Default.Path + "\\" + filename;
            string toFileAndPath = Properties.Settings.Default.CopyLocation + "\\" + filename;
            if (!File.Exists(toFileAndPath))
            {
                FileInfo imgInfo = new FileInfo(fromFileAndPath);
                bool copied = false;
                int counter = 0;
                while (!copied && counter < 100) //While was added as I was getting "The process cannot access the file because it is being used by another process" errors.
                {
                    try
                    {
                        counter++;
                        imgInfo.CopyTo(toFileAndPath);
                        copied = true;
                    }
                    catch
                    {
                        //If it cannot copy catch
                    }
                }
                if (counter > 100)
                    throw new Exception("Unable to copy file!");
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error occurred: " + ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
Timbo
+16  A: 

I've used this code for the past several years, and I haven't had any issues with it.

Understand your hesitation about using exceptions, but you can't avoid them all of the time:

protected virtual bool IsFileLocked(FileInfo file)
    {
        FileStream stream = null;

        try
        {
            stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (IOException)
        {
            //the file is unavailable because it is:
            //still being written to
            //or being processed by another thread
            //or does not exist (has already been processed)
            return true;
        }
        finally
        {
            if (stream != null)
                stream.Close();
        }

        //file is not locked
        return false;
    }
ChrisW
This is a great solution, but I have one comment - you may wan't to open the File with access mode FileAccess.Read since ReadWrite will always fail if the file happens to be read-only.
adeel825
Thanks, that's a good point.
ChrisW
Good answer, but a problem is that the file may not actually exist which will cause a FileNotFound exception - this is an IOException. The IOException catch block will catch this and the method result interprets this as file locked, when in fact the file does not actually exist - this could lead to hard to diagnose bugs.Tricky one from an API perspective - perhaps the API should be GetFileState which returns an enum: Locked, NotFound, KnockYourselfOut. :) Of course an UnauthorizedAccessException could be raised, which is not an IOException...
chibacity
-1. This is a poor answer, because the file could become locked by another thread/process after it is closed in IsFileLocked, and before your thread gets a chance to open it.
ShellShock