970

I'm writing 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?

Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
Dawsy
  • 10,249
  • 5
  • 20
  • 13
  • 34
    All right, you can test it by examining all the open handles on the system. However, since Windows is a multitasking operating system, there is a chance that right after you run the code to determine if the file is open and you deem it is not, a process code start using that file, then by the time you try to use it, you receive an error. But, there is nothing wrong with checking first; just don't assume it is not in use when you actually need it. – BobbyShaftoe May 18 '09 at 06:45
  • 6
    But just for this specific issue; I'd recommend not examining the file handles and just try some preset number of times, say 3-5 before failing. – BobbyShaftoe May 18 '09 at 06:46
  • How is this image file generated? Can you stop/sleep/pause your program until the generation is completed? That is by far a superior way to handle the situation. If not, then I don't think you can avoid using exception handling. – Catchwa May 18 '09 at 06:48
  • Isn't all use of exceptions a check on some assumption by doing something potentially dangerous while deliberately not ruling out the possibility of failure? – jwg Jan 30 '13 at 16:02
  • 39
    Your philosophy has a bad understanding of exceptions. Most people think exceptions means holy-crap-out-of-doom-something's-wrong-die-die-die. When exception means.... exception. It means something exceptional occurred that you need to "handle" (or account for). Maybe you want to keep retrying for data access, maybe the user needs to know that you can't get a connection. What do you do? You handle the ConnectionFailedException and notify the user, so maybe, they'll stop trying after an hour, and notice the cable is unplugged. – Lee Louviere Mar 11 '13 at 15:28
  • Why have the checking and processing in different procedures? try {open file in FileShare.None; process file; close file} catch {filelocked) {//locked, we'll get it next time} – Leif Neland Jun 23 '17 at 08:36
  • 2
    Lee Louviere the op has a valid dislike for working with exceptions. If you can easily use filexists method to know if a file exists what similar command exists to know if the file you want to work with is in use? In fact i believe that is the question the op is really asking. – webs Oct 20 '18 at 10:27
  • 1
    @webs Some exceptions are just a fact of life. Using File.Exists() is good, but like the answer says, it's not always possible to avoid them. For example, a file could exist between the time you check if it exists and when you actually open it. That indefinite period is good enough most of the time, but there are no guarantees and so an exception handler should wrap the call to open it, etc. – Justine Krejcha May 12 '20 at 14:00
  • 1
    @webs *If you can easily use filexists method to know if a file exists what similar command exists to know if the file you want to work with is in use?* A bit late here, but both of those examples are [TOCTOU bugs](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use). The results are immediately invalid. "Check-then-do" is useless - the "check" can not be identical to the "do", so the "check" can fail for different reasons or succeed when the "do" would fail. When you need to open a file ***OPEN THE FILE***. Nothing else can give you definitive proof that opening will work. – Andrew Henle Jul 02 '20 at 13:20
  • 2
    What if I do not want to figure out if I CAN write to a file at all, but if i should rather not although i could, because someone else is currently working on that same file? – ChrisMercator Aug 17 '20 at 08:12

20 Answers20

665

Updated NOTE on this solution: Checking with FileAccess.ReadWrite will fail for Read-Only files so the solution has been modified to check with FileAccess.Read.

ORIGINAL: 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)
{
    try
    {
        using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    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;
    }

    //file is not locked
    return false;
}
Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71
ChrisW
  • 9,151
  • 1
  • 20
  • 34
  • 69
    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 Jan 29 '10 at 01:28
  • 9
    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... – Tim Lloyd May 26 '10 at 15:56
  • 241
    -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. – Polyfun May 26 '10 at 15:58
  • Albeit it works in this case, this answer doesn't apply to other situations. For example, if you want to copy a file, this code may raise an exception, whereas System.Copy wouldn't have complained. Ideas on how to circumvent this problem? – Clément Mar 30 '11 at 16:11
  • CFP, even though you can copy an open file with System.Copy, it's up to you to determine whether the locked file would contain valid data or not. If yes, then you probably don't have to check.The safest approach would be to check whether the file is open/locked, then proceed with File.Copy(). – ChrisW Mar 31 '11 at 06:00
  • Possibly dumb question, but if you return true in the catch, won't the finally block not get called? Is that a problem? – Matthew Aug 10 '11 at 18:09
  • 5
    Matthew, the finally is always called, whatever happens. Always. Dot. :) – Ignacio Soler Garcia Nov 10 '11 at 11:45
  • @adeel825 will changing it to read cause any side-effect? For example, being unable to detect files that are only write-locked? – Louis Rhys Jan 30 '12 at 03:52
  • @SoMoS actually, the finally block is NOT always executed, there's some exceptions that stop your flow (think ThreadAbortException, StackOverflowException, OutOfMemoryException) – Wiebe Tijsma Feb 08 '12 at 13:47
  • @Zidad: if these exceptions happens outside the finally block then the finally will get executed. If they happens inside the finally the flow breaks but anyway you were already executing the finally so the finally always is executed (but maybe it does not end the execution). – Ignacio Soler Garcia Feb 08 '12 at 21:33
  • @SoMoS No that's not entirely correct, but you were right about the ThreadAbortException, I didn't know that, but see this SO question about the other exceptions: http://stackoverflow.com/questions/107735/stackoverflowexception-in-net – Wiebe Tijsma Feb 14 '12 at 15:00
  • 3
    small note: you might consider use using block for automatic disposing, or at least, dispose the stream by yourself. – itsho Jun 29 '12 at 15:55
  • 21
    I think this is a great answer. I'm using this as an [extension method](http://msdn.microsoft.com/en-us/library/bb383977.aspx) á la `public static bool IsLocked(this FileInfo file) {/*...*/}`. – Manuzor Jul 04 '12 at 11:06
  • 57
    @ChrisW: you might be wondering what is going on. Do not be alarmed. You're just being subject to the wrath of the Daily WTF community: http://thedailywtf.com/Comments/The-Right-Way-to-Find-a-File.aspx#402913 – Pierre Lebeaupin Mar 11 '13 at 13:15
  • 2
    @PierreLebeaupin: And why wouldn't that be alarming? – Fred Larson Mar 11 '13 at 14:10
  • 19
    @ChrisW Why is that a bad thing. This community is here to point out good and bad answers. If a bunch of professionals notice this is a bad thing, and join to downvote, then the site is WAI. And before you get negative, if you read that article, they say to "upvote the right answer" not downvote the wrong one. Do you want them to explain their upvotes in comments as well. Thanks for introducing me to another good site! – Lee Louviere Mar 11 '13 at 15:12
  • 5
    anything using this method is itself suseptable to race conditions. E.g., If I call this function and then proceed to try and write to the file it'll fail. Any writes to a file should be handled directly and not call "check if a file is X" logic immediately beforehand. The function itself is "correct" but it's usage by the unwary/inexperienced will leave to subtle errors – DiskJunky Mar 12 '13 at 13:00
  • http://msdn.microsoft.com/en-us/library/vstudio/system.io.ioexception(v=vs.100).aspx – Jeremy Apr 29 '14 at 20:41
  • 1
    Catching IOException does not necessarily indicate a locked file, because I believe you'll catch all exceptions inheriting from IOException which include DirectoryNotFoundException, DriveNotFoundException, EndOfStreamException, FileLoadException, FileNotFoundException, etc. http://msdn.microsoft.com/en-us/library/vstudio/system.io.ioexception(v=vs.100).aspx – Jeremy Apr 29 '14 at 20:43
  • 1
    "Understand your hesitation about using exceptions, but you can't avoid them all of the time" er really? my entire career seems to have not required me to ever use them... only endure them out of politeness. – jheriko Oct 28 '14 at 17:42
  • What if you don't have write access to the folder that the file is in? – Nick Nov 03 '14 at 13:51
  • 1
    The stream object doesn't get Disposed in this solution. Better to wrap it in a using statement. – Paul Taylor Aug 27 '15 at 10:35
  • This solution is less robust than [Spence's one](http://stackoverflow.com/a/876513/1178314) but looks more versatile: in some use cases, your file manipulating code is a black box working from file name, maybe even in a separated process, and Spence's solution looks unusable in such case. – Frédéric Sep 25 '15 at 07:37
  • 1
    @PaulTaylor, [close and dispose](http://stackoverflow.com/a/7525134) do the same on stream objects, and it is [documented](http://stackoverflow.com/a/7525138/1178314). This code does dispose the stream. `using` block with nothing to do on the stream would lead to some strange code in this case. Though I tend to always use `using` on `IDisposable`, I think this case is way more readable without a `using`. – Frédéric Sep 25 '15 at 08:01
  • I've added a more comprehensive way to check for file locks and documented the limitations below: http://stackoverflow.com/a/33150038 – rboy Oct 15 '15 at 14:29
  • 6
    While Polyfun has a point with the possibility of the file becoming locked in the small moment when IsFileLocked returns true and the program actually reopening the file. But that can easily be circumvented by just returning the already opened stream handle instead and a null in case it's locked. Functionally the same as you can simply null check before using it but it keeps the file open since checking. – BloodyRain2k Jun 24 '16 at 03:23
  • 1
    Why is this method `virtual`, not `static`? – Mr Anderson Jul 18 '16 at 20:42
  • 4
    -1 this is a straight up incorrect answer. This only checks if this file is in use "right now" meaning that immediately after you call it, it may immediately be in use by another process and you'll still get the exception. @BloodyRain2k, you are correct, but that isn't pointed out in the answer which still makes it an incorrect answer. – akousmata Jun 26 '17 at 16:17
  • 1
    Aside from the race condition mentioned by @akousmata and also triggering the Daily WTF scrutiny, there's a much more practical issue that is this caused the file to be opened, closed, and then presumably re-opened again, once the file is determined to be accessible. While this is a relatively small overhead, it doubles your trips to disk. It would be much more efficient to utilize the try/catch on the actual operation being performed. – Vinney Kelly Apr 05 '18 at 12:15
  • Thanks for this, works perfectly well for my application (the race issue is not pertinent): external process is instructed to generate file, file is locked status polled, file is read, file is deleted, repeat. – PJRobot Apr 11 '18 at 13:45
  • This kind of file check is usually bad, due to all of the previously mentioned reasons, like race conditions, security vulnerabilities, etc. But sometimes, this is the right solution. In my case, I'm converting a WAV to MP3 using lame.exe, but only after another program is done writing to WAV file. There are no security concerns, nor other programs trying to open the same file. – Mr. TA May 29 '18 at 19:23
  • what about you give us an example we can copy and paste to our vb code? – webs Oct 20 '18 at 10:32
  • 2
    Don't know if this post is still being monitored, but I'll give it a try: we are using a quite similar code (possibly it's yours) and it works fine for small files. But recently we are facing a problem when it comes to processing very large files (several GB): Files are copied from server A to server B, where we fetch them and copy them to Folder X, which is our processes input folder. From time to time our process copies a file to folder X even if this file has not yet been completely copied from Server A to Server B - and I can't find out WHY!!! Any help would be highly appreciated. – Tyron78 Oct 24 '18 at 15:27
  • In addition to race conditions described above this "check" is unusable for me because it actually blocks the file for some amount of time if it is not locked. So other apps/threads may raise an exception while trying to access the file. Better to use Restart Manager API, see e.g. https://stackoverflow.com/questions/1304/how-to-check-for-file-lock?noredirect=1&lq=1#answer-20623302 – Michal Oct 22 '19 at 10:42
  • Does anyone know the solution in node.js(Specifically in electron.js)? – Debotos Das Jun 23 '21 at 05:05
  • "Do the operation instead, then handle the exception". Sure, if it's the one file. In my case I need to process a number of files, but if one of them is in use, nothing at all should be done and the user notified. So I need to check all files first, then if all is ok, go ahead and perform the operations. Race-condition? I still handle any exceptions when doing the actual operations so I don't see the issue there. – CodeOrElse Jun 30 '21 at 14:38
  • As already pointed out by @Tyron78, this method doesn't work when copying files over network. I tested this in Linux (NAS), and I could open a stream no problem, and even write to it (!), while the copying was still executing. So my way in Linux is to check for handles: run 'lsof -t /path/to/file', and check if output was empty. – lisz Jul 31 '22 at 13:46
  • My only issue was that a method named `IsFileLocked` returns true if the file doesn't exist... I feel like that's a special/odd use-case to put in a generic solution – PandaWood Nov 16 '22 at 22:36
609

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 tries to get the file handle.

try
{
   using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
   {
        // File/Stream manipulating code here
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}
psaxton
  • 1,693
  • 19
  • 24
Spence
  • 28,526
  • 15
  • 68
  • 103
  • 149
    +1. There is no 100% safe way to "learn if a file is in use" because milliseconds after you do the check, the file may not be in use anymore, or vice versa. Instead, you just open the file and use it if there are no exceptions. – Sedat Kapanoglu Jan 04 '11 at 07:16
  • 10
    Too bad .NET doesn't support CAS. Something like, TryOpenFile(Ref FileHandle) that returns success/failure. There should always be a work-around not rely on exception handling alone. I wonder how Microsoft Office does it. – TamusJRoyce Jul 31 '11 at 22:37
  • 3
    The key thing to understand here is that this API is simply using the windows API to get a file handle. As such they need to translate the error code received from the C API and wrap it to an exception to throw. We have exception handling in .Net so why not use it. That way you can write a clean forward path in your code and leave the error handling in a separate code path. – Spence Aug 01 '11 at 23:52
  • People who put usings in try/catch blocks confuse me. Just use a try/catch/finally.... – Ben Hughes Nov 26 '12 at 08:17
  • 41
    The using statement is to ensure the stream is closed after I'm done. I think you'll find that the using() {} is less characters than try {} finally { obj.Dispose() }. You'll also find that you now need to declare your object reference outside the using statement, which is more typing. If you have a explicit interface you'd also have to cast. Finally you want to dispose ASAP, and the finally logic may have UI or any other long running actions that have little to do with calling IDispose. – Spence Nov 27 '12 at 20:01
  • The last point is a little artificial. Either you do have UI calls in your finally clause, in which case you do, or you don't, in which case you don't. – jwg Jan 30 '13 at 15:50
  • 3
    that doesn't negate the fact that you have to declare your object outside the try and have to explicitly call dispose, which using does for you and means the same thing. – Spence Jan 31 '13 at 00:27
  • Doesn't this have the same problem as the other answer, i.e., it throws the handle away instead of saving it for whatever the programmer actually wanted to do with the file? – Harry Johnston Mar 12 '13 at 10:11
  • 3
    @HarryJohnston `// File/Stream manipulating code here` - you're supposed to use the file (read/write/etc.) within the `try` block, thereby avoiding the race condition where another process can lock the file between the check and open - because the check and open is one atomic operation here. – Bob Mar 13 '13 at 10:57
  • @Bob: that comment wasn't part of the answer until yesterday. :-) It makes it a lot clearer. – Harry Johnston Mar 14 '13 at 02:50
  • 1
    @BenHughes Don't be confused, do some research. One of .NET's biggest strengths is garbage collection for managed resources. `using` blocks should be used 99.9% of the time for anything implementing IDisposable. As you appear to possibly know: `using` blocks pretty much just IL down to a try/finally with a x.Dispose() call. But that doesn't include catching or handling exceptions. So the one or so lines of code that might actually need handling (the FileStream with File.Open in this case) should be wrapped to catch. Breaking the using into a custom try/catch/finally adds smell. – Suamere Sep 03 '14 at 22:16
  • This solution is more robust than [currently accepted one](http://stackoverflow.com/a/937558/1178314) but looks less versatile to me: in some cases, the file manipulating code is a black box working from file name, maybe even in a separated process, and this solution looks unusable in such case. – Frédéric Sep 25 '15 at 07:34
  • @Frederic: in that scenario, we shouldn't need to do anything. The black box is responsible for dealing with the possibility that the file is in use, not the caller. (It's true that if you're stuck dealing with an incompetently written black box, you might need a best effort workaround, but that would be a different question.) – Harry Johnston Oct 16 '15 at 19:56
  • 2
    @HarryJohnston Incompetently written black box? Not the point. Try opening with Ms Excel a xls file still being written to. Should we say it has been incompetently written for that reason? No, we just wait as best as we can for the file to be no more in use, rather than inflicting to the user a "file locked error => please try again". Both solutions here are useful, depending on the real case. My comment was meant to counter statements such as 'This is a poor answer' about ChrisW answer. – Frédéric Oct 16 '15 at 20:32
  • @Frederic: it is a poor answer to this particular question. It might be a good answer to a question about launching Excel. – Harry Johnston Oct 16 '15 at 20:47
  • 1
    Sorry just to be clear, you would rather risk a security violation with something that "almost" works, as opposed to something that works reliably and correctly in every case? You aren't required to bother the user, but what exactly are you meant to do if your app can't access the file it needs? Or worse, believe the file is available and then incorrectly progresses forward without access to a file? – Spence Oct 26 '15 at 01:06
102

Use this to check if a file is locked:

using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;

private static bool IsFileLocked(Exception exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}

internal static bool CanReadFile(string filePath)
{
    //Try-Catch so we dont crash the program and can check the exception
    try {
        //The "using" is important because FileStream implements IDisposable and
        //"using" will avoid a heap exhaustion situation when too many handles  
        //are left undisposed.
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
            if (fileStream != null) fileStream.Close();  //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
        }
    }
    catch (IOException ex) {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex)) {
            // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
            return false;
        }
    }
    finally
    { }
    return true;
}
}

For performance reasons I recommend you read the file content in the same operation. Here are some examples:

public static byte[] ReadFileBytes(string filePath)
{
    byte[] buffer = null;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;  // sum is a buffer offset for next reading

            fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }
    return buffer;
}

public static string ReadFileTextWithEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            //Depending on the encoding you wish to use - I'll leave that up to you
            fileContents = System.Text.Encoding.Default.GetString(buffer);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    { }     
    return fileContents;
}

public static string ReadFileTextNoEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) 
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            char[] chars = new char[buffer.Length / sizeof(char) + 1];
            System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
            fileContents = new string(chars);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }

    return fileContents;
}

Try it out yourself:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
Flexo
  • 87,323
  • 22
  • 191
  • 272
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
  • 13
    I would upvote if there weren't so many "magic numbers" in there http://en.wikipedia.org/wiki/Magic_number_(programming) – Kris Mar 11 '13 at 21:27
  • 3
    I was referring to the errorCode comparisons, not the bit shifts. though now you mention it... – Kris Mar 15 '13 at 09:35
  • 1
    Your Catch should be on `IOException`, instead of on general `Exception` and then a test on type. – Askolein Apr 22 '13 at 14:31
  • 3
    @JeremyThompson sadly you put the specific `IOException` after the general one. The general one will catch everything passing by and the specific `IOException` will always be lonely. Just swap the two. – Askolein Jun 18 '13 at 13:48
  • I like this solution. One other suggestion: inside the catch as an else to the if(IsFileLocked(ex)) I would throw ex. This will then handle the case where the file does not exist (or any other IOException) by throwing the exception. – shindigo Apr 18 '14 at 20:33
  • If you want to open the file while another process has it open, specify `FileAccess fileAccess = FileAccess.Read, FileShare fileShare = FileShare.ReadWrite` – Jeremy Thompson Sep 05 '16 at 02:22
  • What's the purpose of the bit-masking, when in the next line you compare explicitly with a constant? Also, `GetHRForException` has side effects - starting with .NET 4.5 the `HResult` property is public. – BartoszKP Apr 18 '17 at 15:16
13

I recently came across this issue and found this: https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors.

Here, Microsoft describes the following method for checking if an IOException was due to a locked file:

catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
    Console.WriteLine("There is a sharing violation.");
}
asdf101
  • 569
  • 5
  • 18
  • 1
    Best answer so far. Working for locked files only, not general IO exceptions. – Azelski Jun 13 '21 at 19:29
  • This only does *ERROR_SHARING_VIOLATION*, see my answer for the **ERROR_LOCK_VIOLATION** as well which is the more common cause of this issue. – Jeremy Thompson Apr 14 '23 at 08:11
  • @JeremyThompson because that is what the question asked, they wanted to avoid the "file in use by another process" error. If you also want to catch lock violations you can simply check for `(e.HResult & 0x0000FFFF) == 33` as well, imho your answer is overcomplicated. – asdf101 Apr 15 '23 at 09:09
8

Just use the exception as intended. Accept that the file is in use and try again, repeatedly until your action is completed. This is also the most efficient because you do not waste any cycles checking the state before acting.

Use the function below, for example

TimeoutFileAction(() => { System.IO.File.etc...; return null; } );

Reusable method that times out after 2 seconds

private T TimeoutFileAction<T>(Func<T> func)
{
    var started = DateTime.UtcNow;
    while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
    {
        try
        {
            return func();                    
        }
        catch (System.IO.IOException exception)
        {
            //ignore, or log somewhere if you want to
        }
    }
    return default(T);
}
Jay Byford-Rew
  • 5,736
  • 1
  • 35
  • 36
7

The accepted answers above suffer an issue where if file has been opened for writing with a FileShare.Read mode or if the file has a Read-Only attribute the code will not work. This modified solution works most reliably, with two things to keep in mind (as true for the accepted solution also):

  1. It will not work for files that has been opened with a write share mode
  2. This does not take into account threading issues so you will need to lock it down or handle threading issues separately.

Keeping the above in mind, this checks if the file is either locked for writing or locked to prevent reading:

public static bool FileLocked(string FileName)
{
    FileStream fs = null;

    try
    {
        // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
        fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
    }
    catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
    {
        // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
        try
        {
            fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (Exception)
        {
            return true; // This file has been locked, we can't even open it to read
        }
    }
    catch (Exception)
    {
        return true; // This file has been locked
    }
    finally
    {
        if (fs != null)
            fs.Close();
    }
    return false;
}
rboy
  • 2,018
  • 1
  • 23
  • 35
  • Still has the same problem as the accepted answer - it only tells you whether the file was locked by another process *at one particular moment in time*, which is not useful information. By the time the function has returned the result may already be out of date! – Harry Johnston Oct 16 '15 at 19:57
  • 2
    that's true, one can only check at any given moment of time (or subscribe to events), the advantage of this approach over the accepted solution is that it can check for a read only attribute and a write lock and not return a false positive. – rboy Oct 17 '15 at 03:03
7

You can return a task which gives you a stream as soon as it becomes available. It's a simplified solution, but it is a good starting point. It's thread safe.

private async Task<Stream> GetStreamAsync()
{
    try
    {
        return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
    }
    catch (IOException)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await GetStreamAsync();
    }
}

You can use this stream as usual:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
Ivan Branets
  • 604
  • 6
  • 7
  • 14
    How many seconds until a stack overflow from the recursion in `GetStreamAsync()`? – CAD bloke Jul 24 '17 at 11:14
  • @CADbloke, you have raised a very good point. Indeed my sample might have stack overflow exception, in case if a file is not available for a long time. Related to this answer https://stackoverflow.com/questions/4513438/c-sharp-recursion-depth-how-deep-can-you-go, it might raise the exception in 5 hours. – Ivan Branets Jul 26 '17 at 08:51
  • 1
    Related to your use cases it is preferable to throw I/O exception if let's say 10 attempts to read the file have been failed. The other strategy might be increasing waiting time for a second once 10 attempts have been failed. You can also use a mix of both. – Ivan Branets Jul 26 '17 at 08:59
  • I would (and do) simply alert the user the file is locked. They generally locked it themselves so they will probably do something about it. Or not. – CAD bloke Jul 26 '17 at 12:25
  • In some cases, you need to use retry policy since a file might not be ready yet. Imagine a desktop application to download images in some temporary folder. The application starts downloading and at the same time you open this folder in file explorer. Windows wants to create a thumbnail immediately and lock the file. At the same time, your app tries to replace the locked image to some other place. You will receive an exception if you don't use retry policy. – Ivan Branets Jul 31 '17 at 13:16
  • agreed. AutoCAD is a use-case for this sort of thing. When it saves a file it 1. saves the current drawing as a temp file, 2. deletes the old *.bak backup file, 3. renames the *.dwg file to *.bak, 4. renames the new temp file as *.dwg. At no stage does it check for write locks so it often fails on file systems and networks with a lot of latency. Someone should point them at this page. – CAD bloke Jul 31 '17 at 21:52
  • I do something like this instead of delay I schedule a future task. – danny117 Feb 19 '18 at 18:36
  • FYI: RE: The point about recursion; you could just use add `while(!cancellationToken.IsCancellationRequested){/* your try catch blocks here */} cancellationToken.ThrowIfCancellationRequested();` around your existing code to avoid the need for recursion. – JohnLBevan Nov 05 '21 at 10:09
7

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.

Karl Johan
  • 4,012
  • 1
  • 25
  • 36
  • 1
    FileSystemWatcher doesn't handle lots of changes at the same time very well though, so be careful with that. – Ben F Jun 20 '12 at 07:59
  • 1
    BTW, have you guys noticed while debugging and watching threads that MS calls their own FSW "FileSystemWather"? What's a wather anyway? – devlord Apr 22 '13 at 00:29
  • 4
    Using a FileSystemWatcher does not help, because the Created and Changed events raise at the beginning of a file creation/change. Even small files need more time to be written and closed by the operating system than the .NET application needs to run through the FileSystemEventHandler Callback. This is so sad, but there is no other option than to estimate the wait time before accessing the file or run into exception loops... –  Jul 08 '11 at 12:29
  • 2
    Precisely I got this problem because a windows service with FileSystemWatcher try to read the file before the process closed it. – freedeveloper Aug 08 '18 at 15:09
5

Here is some code that as far as I can best tell does the same thing as the accepted answer but with less code:

    public static bool IsFileLocked(string file)
    {
        try
        {
            using (var stream = File.OpenRead(file))
                return false;
        }
        catch (IOException)
        {
            return true;
        }        
    }

However I think it is more robust to do it in the following manner:

    public static void TryToDoWithFileStream(string file, Action<FileStream> action, 
        int count, int msecTimeOut)
    {
        FileStream stream = null;
        for (var i = 0; i < count; ++i)
        {
            try
            {
                stream = File.OpenRead(file);
                break;
            }
            catch (IOException)
            {
                Thread.Sleep(msecTimeOut);
            }
        }
        action(stream);
    }
cdiggins
  • 17,602
  • 7
  • 105
  • 102
5

Aside from working 3-liners and just for reference: If you want the full blown information - there is a little project on Microsoft Dev Center:

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

Now found at: https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs

From the Introduction:

The C# sample code developed in .NET Framework 4.0 would help in finding out which is the process that is having a lock on a file. RmStartSession function which is included in rstrtmgr.dll has been used to create a restart manager session and according to the return result a new instance of Win32Exception object is created. After registering the resources to a Restart Manager session via RmRegisterRescources function, RmGetList function is invoked to check what are the applications are using a particular file by enumerating the RM_PROCESS_INFO array.

It works by connecting to the "Restart Manager Session".

The Restart Manager uses the list of resources registered with the session to determine which applications and services must be shut down and restarted. Resources can be identified by filenames, service short names, or RM_UNIQUE_PROCESS structures that describe running applications.

It might be a little overengineered for your particular needs... But if that is what you want, go ahead and grab the vs-project.

Bernhard
  • 2,541
  • 1
  • 26
  • 24
5
static bool FileInUse(string path) {
    try {
        using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate)) {
            return !fs.CanWrite;
        }
        //return false;
    }
    catch (IOException ex) {
        return true;
    }
}

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse = FileInUse(filePath);

// Then you can do some checking
if (isFileInUse)
   Console.WriteLine("File is in use");
else
   Console.WriteLine("File is not in use");

Hope this helps!

user16217248
  • 3,119
  • 19
  • 19
  • 37
Julian
  • 97
  • 1
  • 2
  • 13
    The actual check you perform is fine; putting it inside a function is misleading. You do NOT want to use a function like this prior to opening a file. Inside the function, the file is opened, checked, and closed. Then the programmer ASSUMES the file is STILL ok to use and tries opening it for use. This is bad because it could be used and locked by another process that was queued up to open this file. Between the 1st time it was opened (for checking) and the 2nd time it was opened (for use), the OS could have descheduled your process and could be running another process. – Lakey Mar 25 '13 at 19:50
4

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
  • 9,209
  • 2
  • 28
  • 29
3

In my experience, you usually want to do this, then 'protect' your files to do something fancy and then use the 'protected' files. If you have just one file you want to use like this, you can use the trick that's explained in the answer by Jeremy Thompson. However, if you attempt to do this on lots of files (say, for example when you're writing an installer), you're in for quite a bit of hurt.

A very elegant way this can be solved is by using the fact that your file system will not allow you to change a folder name if one of the files there it's being used. Keep the folder in the same file system and it'll work like a charm.

Do note that you should be aware of the obvious ways this can be exploited. After all, the files won't be locked. Also, be aware that there are other reasons that can result in your Move operation to fail. Obviously proper error handling (MSDN) can help out here.

var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));

try
{
    Directory.Move(originalFolder, someFolder);

    // Use files
}
catch // TODO: proper exception handling
{
    // Inform user, take action
}
finally
{
    Directory.Move(someFolder, originalFolder);
}

For individual files I'd stick with the locking suggestion posted by Jeremy Thompson.

atlaste
  • 30,418
  • 3
  • 57
  • 87
  • Hi, Since the order of answers changes can you clarify which post **above** you mean for readers of this popular QA. Thanks. – Jeremy Thompson Nov 21 '14 at 11:02
  • 1
    @JeremyThompson You're right, thanks, I'll edit the post. I'd use the solution from you, mainly because of your correct use of `FileShare` and checking for a lock. – atlaste Nov 21 '14 at 11:09
3

I once needed to upload PDFs to an online backup archive. But the backup would fail if the user had the file open in another program (such as PDF reader). In my haste, I attempted a few of the top answers in this thread but could not get them to work. What did work for me was trying to move the PDF file to its own directory. I found that this would fail if the file was open in another program, and if the move were successful there would be no restore-operation required as there would be if it were moved to a separate directory. I want to post my basic solution in case it may be useful for others' specific use cases.

string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
    fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
    open_elsewhere = true;
}

if (open_elsewhere)
{
    //handle case
}
2

You can use my library for accessing files from multiple apps.

You can install it from nuget: Install-Package Xabe.FileLock

If you want more information about it check https://github.com/tomaszzmuda/Xabe.FileLock

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}

fileLock.Acquire method will return true only if can lock file exclusive for this object. But app which uploading file must do it in file lock too. If object is inaccessible metod returns false.

Tomasz Żmuda
  • 629
  • 6
  • 9
0

Would something like this help?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter("filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}
Ouroborus
  • 16,237
  • 4
  • 39
  • 62
tedi
  • 6,350
  • 5
  • 52
  • 67
0

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
  • 17,808
  • 7
  • 62
  • 75
  • @jcolebrand locks what? the one you copied? Or the one you put in the temp dir? – Cullub Aug 07 '14 at 17:23
  • 5
    if you copy the file, expecting nobody else to be working on it, and you're going to use the temp file, and then someone locks it right after you copy it, then you've potentially lost data. – jcolebrand Aug 07 '14 at 21:07
  • Tried it just before (copying a large file from local harddisk to a virtual drive on a virtual server (1 minute) - trying to detect end of that copying with attempts of File.Move() - but it failed! Now file exists in both directories... and it attemted it to copy 3 times at the end... – BitLauncher Apr 13 '21 at 20:20
-1

I'm interested to see if this triggers any WTF reflexes. I have a process which creates and subsequently launches a PDF document from a console app. However, I was dealing with a frailty where if the user were to run the process multiple times, generating the same file without first closing the previously generated file, the app would throw an exception and die. This was a rather frequent occurrence because file names are based on sales quote numbers.

Rather than failing in such an ungraceful manner, I decided to rely on auto-incremented file versioning:

private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
    try
    {
        var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
        using (var writer = new FileStream(filePath, FileMode.Create))
        {
            writer.Write(data, 0, data.Length);
        }
        return filePath;
    }
    catch (IOException)
    {
        return WriteFileToDisk(data, fileName, ++version);
    }
}

Probably some more care can be given to the catch block to ensure I'm catching the correct IOException(s). I'll probably also clear out the app storage on startup since these files are intended to be temporary anyways.

I realize this goes beyond the scope of the OP's question of simply checking if the file is in use but this was indeed the problem I was looking to solve when I arrived here so perhaps it will be useful to someone else.

Vinney Kelly
  • 4,975
  • 1
  • 25
  • 31
-1
retry_possibility:
//somecode here

try
{
    using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
    {
        stream.Close();
    }
    //write or open your file here
}
catch (IOException)
{
    DialogResult dialogResult = MessageBox.Show("This file is opened by you or another user. Please close it and press retry.\n"+ expFilePath, "File Locked", MessageBoxButtons.RetryCancel);
    if (dialogResult == DialogResult.Retry)
    {
        goto retry_possibility;
    }
    else if (dialogResult == DialogResult.Cancel)
    {
        //do nothing
    }
}
Ribaz
  • 476
  • 3
  • 10
-1

I use this workaround, but i have a timespan between when i check the file locking with IsFileLocked function and when i open the file. In this timespan some other thread can open the file, so i will get IOException.

So, i added extra code for this. In my case i want load XDocument:

        XDocument xDoc = null;

        while (xDoc == null)
        {
            while (IsFileBeingUsed(_interactionXMLPath))
            {
                Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
                Thread.Sleep(100);
            }
            try
            {
                xDoc = XDocument.Load(_interactionXMLPath);
            }
            catch
            {
                Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
            }
        }

What do you think? Can i change some thing? Maybe i did not have to use IsFileBeingUsed function at all?

Thanks

zzfima
  • 1,528
  • 1
  • 14
  • 21
  • 5
    what is IsFileBeingUsed ? source code about IsFileBeingUsed ? – Kiquenet Jul 30 '13 at 06:01
  • 1
    IsFileBeingUsed - from the accepted answer. he is just offering a way how to use it if you want to get access to the file eventually.. nothing wrong with this answer. – Boppity Bop May 05 '21 at 17:11