14

Ok, so I have searched in many places for the answer to this question, but I'm open to any links if I missed something obvious.

I am interested in producing reasonable error messages to the user when they attempt to open a particular file, but for whatever reason the program cannot access that file. I would like to distinguish between the following cases:

  • The file was locked by another process such that this process cannot write to it.
  • The user does not have the appropriate access privileges to write to the file (as in, their user permissions, as seen in the Properties screen for a file in Windows explorer, do not give the user write permission)
  • The file requires "elevated" permission to access the file.

I am using a FileStream object. I have looked at the msdn documentation for instantiating a FileStream, and it is not at all clear to me which Exception does what for the above, and how to distinguish between them. I admit that my experience with Windows programming is limited, so I may be missing something obvious. My apologies if so.

Ken White
  • 123,280
  • 14
  • 225
  • 444
skybluecodeflier
  • 1,339
  • 13
  • 26
  • 1
    Is there a difference between the last 2 cases you want to check for? I would imagine (though haven't proven) that they would both throw a `SecurityException` where the first case would throw an `IOException`. – M.Babcock Feb 21 '12 at 00:49
  • @M.Babcock: In some ways, the last two cases aren't very different. However, the user has to (in general) do very different things in order to get access to the file. For the 2nd case, they might need to request that another user give them permission. For the 3rd case, they need to "Run as Administrator" or perhaps move the file out of the "Program Files" directory (though why it would be in there I don't know). I would like to help the user know what they need to do in order to get access to the file. – skybluecodeflier Feb 21 '12 at 00:57
  • Once again, I may be showing my ignorance of Windows OS in my previous comment- I am open to correction if my interpretation is incorrect. – skybluecodeflier Feb 21 '12 at 00:58
  • It makes sense. It sounds like what you're looking for should be discoverable through a series of tests and examining the exceptions thrown. Just a hunch, you might be able to use the [`HResult`](http://msdn.microsoft.com/en-us/library/system.exception.hresult.aspx) property of the `SecurityException` to determine the underlying cause. – M.Babcock Feb 21 '12 at 01:02

1 Answers1

6

Here's what you could do:

1) You could test if you have rights to access to the file before trying to access to your file. From this SO thread, here is a method that should return true if user has Write rights (i.e. when right-clicking on a file -> property -> security). This covers your point (2) for unappropriated access privileges (do note that there is maybe something more robust/error-proof to get this information than the code below):

public static bool HasWritePermissionOnFile(string path)
{
    bool writeAllow = false;
    bool writeDeny = false;

    FileSecurity accessControlList = File.GetAccessControl(path);
    if (accessControlList == null)
    {
        return false;
    }

    var accessRules = accessControlList.GetAccessRules(true, true, typeof(SecurityIdentifier));
    if (accessRules == null)
    {
        return false;
    }

    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Write & rule.FileSystemRights) != FileSystemRights.Write)
        {
            continue;
        }

        if (rule.AccessControlType == AccessControlType.Allow)
        {
            writeAllow = true;
        }
        else if (rule.AccessControlType == AccessControlType.Deny)
        {
            writeDeny = true;
        }
    }

    return writeAllow && !writeDeny;
}

2) Do try to instantiate your FileStream, and catch exceptions:

try
{
    string file = "...";
    bool hasWritePermission = HasWritePermissionOnFile(file);
    using (FileStream fs = new FileStream(file, FileMode.Open))
    {
    }
}
catch (UnauthorizedAccessException ex)
{
    // Insert some logic here
}
catch (FileNotFoundException ex)
{
    // Insert some logic here
}
catch (IOException ex)
{
    // Insert some logic here
}

In your case (3) (file requires elevation), UnauthorizedAccessException is thrown.

In your case (1) (file is locked by another process), IOException is thrown. You can then check the HRESULT of the exception for more details:

catch (IOException ex)
{
    // Gets the HRESULT
    int hresult = Marshal.GetHRForException(ex);

    // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
    // for system error code
    switch (hresult & 0x0000FFFF)
    {
        case 32:    //ERROR_SHARING_VIOLATION
            Console.WriteLine("File is in use by another process");
            break;
    }
}

Now you should be able to distinguish your 3 use cases.

Community
  • 1
  • 1
ken2k
  • 48,145
  • 10
  • 116
  • 176
  • Hmm. This makes sense- I will test this. Though I don't think I would be able to trust solely in solution 1 that you specified here, because of the possibility of race conditions. That is, a) My process checks to see if it can access the file, b) some other process opens the file (or changes permissions, etc), c) my process tries to open the file and fails/gets cranky/etc. – skybluecodeflier Feb 21 '12 at 15:55
  • Actually there could be a race condition only for case (2) (unappropriated access privileges) if user loses rights between the `HasWritePermissionOnFile` check and the `new FileStream` call. If it occurs, a `UnauthorizedAccessException` exception would be thrown, so you'll still be able to know that "there is an access problem". You could then call `HasWritePermissionOnFile` within the `catch (UnauthorizedAccessException)` block to ensure the exception when thrown because of elevation issue. – ken2k Feb 21 '12 at 16:08
  • Ha, it feels like that would just move the race condition into the catch block for the UnauthorizedAccessException. Though I'm not sure if it is possible to avoid that. Or is there? It definitely works under normal conditions. Thanks! – skybluecodeflier Feb 21 '12 at 16:38
  • Out of curiosity, what would cause the SecurityException to be thrown from trying to instantiate the FileStream? – skybluecodeflier Feb 21 '12 at 16:39
  • @skybluecodeflier True. Well, if some process is constantly changing the security attributes of your file, it'll be hard to precisely know what causes the `UnauthorizedAccessException` exception to be thrown ;) Actually it doesn't look like a common use case to me. – ken2k Feb 21 '12 at 16:47
  • @skybluecodeflier For the SecurityException I suspect the exception to be thrown in some specific cases, for instance if you try to use a file located on a network share with a restrictive group policy configuration. – ken2k Feb 21 '12 at 16:59
  • Ok, I guess I'll have to handle that as well... Thanks so much! – skybluecodeflier Feb 21 '12 at 17:18