none
How to check if a file is in use (when the system apparently behaves erratic if I use some standard way of checking) RRS feed

  • Question

  • Hello community,

    it is really weird and wicked! To check if a file is in use I try the following code

    // Proceed only when the application is closed by the user again
    bool closed = false;
    FileStream testStream;
    FileInfo fileInfoObjectForTest;
    while (!closed)
    {
        try
        {
            fileInfoObjectForTest = new FileInfo(filename);
            testStream = fileInfoObjectForTest.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            closed = true;
            testStream.Close();
        }
        catch (Exception e)
        { }
    }
    
    updatefile();

    i.e., before I invoke the updatefile method I check permanently if the file is in use because I know it is in use and the program has to wait until it is closed. This code works so far but there is a weirdness about it when a program (at least Excel) does a "Save", i.e., when you click the save button in the Excel Client. It is, the program jumps to the updatefile method though the Excel file was not closed but only saved!! Try it for yourself and tell me if you have the same behavior. This means for me that the testStream object can be created for the short period of time when the user clicks on save in the Excel Client (??) .. very peculiar! I searched in the Internet and saw a thread in this Forum with the title "FAQ: How do I check whether a file is in use?" where the first method in the thread is the same I use.

    I also tried the method IsFileLocked in the mentioned forum thread that uses some win32 functionality with the same result as before, that is, when I click on the save button then the program leaves the while loop and jumps to the updatefile method. So the approach with trying to open a used file to get an exception to realise it is used seems not to be applicable for me.

    Do you have any idea why this peculiar behavior happens and could you tell me how to solve this?

    Thanks in advance!

    Sunday, December 21, 2014 2:16 AM

Answers

  • Although I currently do not have Excel to test, I got the gut feeling that you should test access for "~$<filename>.xlsx" instead.

    The main file of Excel is saved using tunneling technique to prevent file got corrupted when the targeting filesystem does not support transacted access, so when you check access on the file directly, you could run into race condition that the file is in fact closed and under rename process. You had better check for file that is not supposed to be released as long as someone is opening the file using Excel first, then check for the file directly to check whether "non-Excel" programs are using it.

    • Proposed as answer by Blackwood Monday, December 22, 2014 4:05 PM
    • Unproposed as answer by Ilker S Monday, December 22, 2014 4:50 PM
    • Marked as answer by Ilker S Monday, December 22, 2014 4:52 PM
    Monday, December 22, 2014 1:57 AM
    Answerer
  • Wow cheong00 you are my hero! :)

    It is exactly as you said. I didn't know of this Tunneling technique and in fact it turns my checking strategy into rubbish. I already knew about those temporary files that Office produces but I didn't fancy that it could have such an Impact on my code someday. Without this knowledge of course I was not able to know how I should cope with the phenomenon, thank you!

    So I expanded the logic of my code accordingly, checking for a temporary file as you suggested. But because my program also opens files that are not Office files (e.g. pdf files) I also had to add logic for normal files. Everything is working fine so far!

    But I think there could arise two possible Problems with this Approach:

    1. Do programs that don't belong to the MS Office package also use "~$<filename>" as a General convention if they want to use the Tunneling technique? If not, then I would have a Problem if the user of my application theoretically opens such a file in the future.

    2. I think I occasionally observed that sometimes the temporary file of an Office file could not be deleted though the file was closed by the program. In such cases I would have an infinite Loop in my program I think.

    Here is my code I produced with including the Information you provided. It works fine so far:

    while (IsFileLocked(filename)) { }
    
    dosomething();
    
    
    public static bool IsFileLocked(string filename)
    {
    	bool fileLocked = true;
    	string fullName = Path.Combine(Path.GetTempPath(), filename);
    	string tempFullname = Path.Combine(Path.GetTempPath(), @"~$" + filename);
    
    
    	try
    	{
    		// Check for the filename as MS Office style temporary name ~$<filename>
    		using (File.Open(tempFullname, FileMode.Open, FileAccess.Write, FileShare.None))
    		{
    			fileLocked = false;
    		}
    	}
    	catch (FileNotFoundException)
    	{
    		// This can happen when the application does not generate an MS Office style temporary file. This can e.g. be a pdf-File.
    		// In the second case this can also mean that the user has closed the MS Office file so the temporary file is deleted by the office application.
    		// In both cases we can try to open the original file for purposes of checking.
    		try
    		{
    			using (File.Open(fullName, FileMode.Open, FileAccess.Write, FileShare.None))
    			{
    				fileLocked = false;
    			}
    		}
    		catch (IOException ex)
    		{
    			// This means that the original file cannot be opened and should be checked if it is being used or locked by another process.
    			fileLocked = EvaluateIOException(ex);
    		}
    	}
    	catch (IOException exception)
    	{
    		// This means that the temporary file cannot be opened and should be checked if it is being used or locked by another process.
    		fileLocked = EvaluateIOException(exception);
    	}
    	catch (Exception)
    	{
    		// Another sort of exception. The file is not used or is locked by another process at all.
    		fileLocked = false;
    	}
    
    	return fileLocked;
    }
    
    private static bool EvaluateIOException(IOException exception)
    {
    	var errorCode = Marshal.GetHRForException(exception) & 65535;
    	return errorCode == 32 || errorCode == 33;
    }




    • Edited by Ilker S Monday, December 22, 2014 3:23 PM
    • Marked as answer by Ilker S Monday, December 22, 2014 4:49 PM
    Monday, December 22, 2014 3:15 PM

All replies

  • public static bool IsFileLocked(string file)
    	{
    	    try
    	    {
    	        using (File.Open(file, FileMode.Open, FileAccess.Write, FileShare.None))
    	        {
    	            return false;
    	        }
    	    }
    	    catch (IOException exception)
    	    {
    	        var errorCode = Marshal.GetHRForException(exception) & 65535;
    	        return errorCode == 32 || errorCode == 33;
    	    }
    	    catch (Exception)
    	    {
    	        return false;
    	    }
    	}

    for more information, see:
    System Error Codes (0-499)
    How to check for file lock?

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    Sunday, December 21, 2014 3:38 AM
  • Hello Bill,

    big thanks for your quick reply. But I presume that you didn't read my posting to the full, otherwise you would see that I have a Problem with exactly the Approach you posted. Nevertheless I tried your method using it in a while Loop like this:

    while (IsFileLocked(filename)) { }
    
    dosomething();
    Nevertheless the program acts as before, i.e., at the latest with the second click on the save button of Excel the program jumps to the dosomething() method though the file is still open in Excel :( Try it yourself!
    Sunday, December 21, 2014 11:22 PM
  • Although I currently do not have Excel to test, I got the gut feeling that you should test access for "~$<filename>.xlsx" instead.

    The main file of Excel is saved using tunneling technique to prevent file got corrupted when the targeting filesystem does not support transacted access, so when you check access on the file directly, you could run into race condition that the file is in fact closed and under rename process. You had better check for file that is not supposed to be released as long as someone is opening the file using Excel first, then check for the file directly to check whether "non-Excel" programs are using it.

    • Proposed as answer by Blackwood Monday, December 22, 2014 4:05 PM
    • Unproposed as answer by Ilker S Monday, December 22, 2014 4:50 PM
    • Marked as answer by Ilker S Monday, December 22, 2014 4:52 PM
    Monday, December 22, 2014 1:57 AM
    Answerer
  • Wow cheong00 you are my hero! :)

    It is exactly as you said. I didn't know of this Tunneling technique and in fact it turns my checking strategy into rubbish. I already knew about those temporary files that Office produces but I didn't fancy that it could have such an Impact on my code someday. Without this knowledge of course I was not able to know how I should cope with the phenomenon, thank you!

    So I expanded the logic of my code accordingly, checking for a temporary file as you suggested. But because my program also opens files that are not Office files (e.g. pdf files) I also had to add logic for normal files. Everything is working fine so far!

    But I think there could arise two possible Problems with this Approach:

    1. Do programs that don't belong to the MS Office package also use "~$<filename>" as a General convention if they want to use the Tunneling technique? If not, then I would have a Problem if the user of my application theoretically opens such a file in the future.

    2. I think I occasionally observed that sometimes the temporary file of an Office file could not be deleted though the file was closed by the program. In such cases I would have an infinite Loop in my program I think.

    Here is my code I produced with including the Information you provided. It works fine so far:

    while (IsFileLocked(filename)) { }
    
    dosomething();
    
    
    public static bool IsFileLocked(string filename)
    {
    	bool fileLocked = true;
    	string fullName = Path.Combine(Path.GetTempPath(), filename);
    	string tempFullname = Path.Combine(Path.GetTempPath(), @"~$" + filename);
    
    
    	try
    	{
    		// Check for the filename as MS Office style temporary name ~$<filename>
    		using (File.Open(tempFullname, FileMode.Open, FileAccess.Write, FileShare.None))
    		{
    			fileLocked = false;
    		}
    	}
    	catch (FileNotFoundException)
    	{
    		// This can happen when the application does not generate an MS Office style temporary file. This can e.g. be a pdf-File.
    		// In the second case this can also mean that the user has closed the MS Office file so the temporary file is deleted by the office application.
    		// In both cases we can try to open the original file for purposes of checking.
    		try
    		{
    			using (File.Open(fullName, FileMode.Open, FileAccess.Write, FileShare.None))
    			{
    				fileLocked = false;
    			}
    		}
    		catch (IOException ex)
    		{
    			// This means that the original file cannot be opened and should be checked if it is being used or locked by another process.
    			fileLocked = EvaluateIOException(ex);
    		}
    	}
    	catch (IOException exception)
    	{
    		// This means that the temporary file cannot be opened and should be checked if it is being used or locked by another process.
    		fileLocked = EvaluateIOException(exception);
    	}
    	catch (Exception)
    	{
    		// Another sort of exception. The file is not used or is locked by another process at all.
    		fileLocked = false;
    	}
    
    	return fileLocked;
    }
    
    private static bool EvaluateIOException(IOException exception)
    {
    	var errorCode = Marshal.GetHRForException(exception) & 65535;
    	return errorCode == 32 || errorCode == 33;
    }




    • Edited by Ilker S Monday, December 22, 2014 3:23 PM
    • Marked as answer by Ilker S Monday, December 22, 2014 4:49 PM
    Monday, December 22, 2014 3:15 PM
  • 1) I'm not aware of other programs using this kind of naming for files, but maybe some home brew programs would use. There's no guarantee but if you're developing for in-house use, there's good chance it should be fine.

    2) Try to use a Thread timer to introduce Timeout for your application. It'd be fine to declare you can't write to the file after some wait, and then notify the user for whether keep waiting, write to another filename, or try again later.


    Tuesday, December 23, 2014 1:41 AM
    Answerer