used - c# wait until file is not in use




How to check for file lock? (8)

Is there any way to check whether a file is locked without using a try/catch block?

Right now, the only way I know of is to just open the file and catch any System.IO.IOException .


Then between the two lines, another process could easily lock the file, giving you the same problem you were trying to avoid to begin with: exceptions.

However, this way, you would know that the problem is temporary, and to retry later. (E.g., you could write a thread that, if encountering a lock while trying to write, keeps retrying until the lock is gone.)

The IOException, on the other hand, is not by itself specific enough that locking is the cause of the IO failure. There could be reasons that aren't temporary.


A variation of DixonD's excellent answer (above).

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           TimeSpan timeout,
                           out Stream stream)
{
    var endTime = DateTime.Now + timeout;

    while (DateTime.Now < endTime)
    {
        if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
            return true;
    }

    stream = null;
    return false;
}

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           out Stream stream)
{
    try
    {
        stream = File.Open(path, fileMode, fileAccess, fileShare);
        return true;
    }
    catch (IOException e)
    {
        if (!FileIsLocked(e))
            throw;

        stream = null;
        return false;
    }
}

private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;

private static bool FileIsLocked(IOException ioException)
{
    var errorCode = (uint)Marshal.GetHRForException(ioException);
    return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}

Usage:

private void Sample(string filePath)
{
    Stream stream = null;

    try
    {
        var timeOut = TimeSpan.FromSeconds(1);

        if (!TryOpen(filePath,
                     FileMode.Open,
                     FileAccess.ReadWrite,
                     FileShare.ReadWrite,
                     timeOut,
                     out stream))
            return;

        // Use stream...
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }
}


No, unfortunately, and if you think about it, that information would be worthless anyway since the file could become locked the very next second (read: short timespan).

Why specifically do you need to know if the file is locked anyway? Knowing that might give us some other way of giving you good advice.

If your code would look like this:

if not locked then
    open and update file

Then between the two lines, another process could easily lock the file, giving you the same problem you were trying to avoid to begin with: exceptions.


What I ended up doing is:

internal void LoadExternalData() {
    FileStream file;

    if (TryOpenRead("filepath/filename", 5, out file)) {
        using (file)
        using (StreamReader reader = new StreamReader(file)) {
         // do something 
        }
    }
}


internal bool TryOpenRead(string path, int timeout, out FileStream file) {
    bool isLocked = true;
    bool condition = true;

    do {
        try {
            file = File.OpenRead(path);
            return true;
        }
        catch (IOException e) {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            condition = (isLocked && timeout > 0);

            if (condition) {
                // we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
                timeout--;
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
            }
        }
    }
    while (condition);

    file = null;
    return false;
}

When I faced with a similar problem, I finished with the following code:

public bool IsFileLocked(string filePath)
{
    try
    {
        using (File.Open(filePath, FileMode.Open)){}
    }
    catch (IOException e)
    {
        var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);

        return errorCode == 32 || errorCode == 33;
    }

    return false;
}


You could call LockFile via interop on the region of file you are interested in. This will not throw an exception, if it succeeds you will have a lock on that portion of the file (which is held by your process), that lock will be held until you call UnlockFile or your process dies.





filelock