How do I determine the HResult for a System.IO.IOException?

34

The System.Exception.HResult property is protected. How can I peek inside an exception and get the HResult without resorting to reflection or other ugly hacks?


Here's the situation:
I want to write a backup tool, which opens and reads files on a system. I open the file with FileAccess.Read and FileShare.ReadWrite, according to this guidance, because I don't care if the file is open for writing at the time I read it.

In some cases, when a file I am reading is open by another app, the System.IO.FileStream.Read() method throws a System.IO.IOException, "The process cannot access the file because another process has locked a portion of the file". This is error 33, or I think HResult 0x80070021. [EDIT: I believe this can be returned when another process calls LockFileEx to lock a byte range within a file.]

I'd like to pause and retry when I get this error. I think this is the appropriate action to take here. If the locking process releases the byte-range lock quickly, then I can proceed reading the file.

How can I distinguish an IOException for this reason, from others? I can think of these ways:

  • private reflection - don't wanna do that. Perf will stink.
  • call Exception.ToString() and parse the string. Feels hacky. Won't work in i18n versions.

I don't like these options. Isn't there a better, cleaner way?


I just searched around and found System.Runtime.InteropServices.Marshal.GetHRForException. Will that return a uint like 0x80070021?

c#
.net
exception
hresult
asked on Stack Overflow Jun 13, 2009 by Cheeso • edited May 23, 2017 by Community

6 Answers

55

For .Net Framework 4.5 and above, you can use the Exception.HResult property:

int hr = ex.HResult;

For older versions, you can use Marshal.GetHRForException to get back the HResult, but this has significant side-effects and is not recommended:

int hr = Marshal.GetHRForException(ex);
answered on Stack Overflow Jun 13, 2009 by JaredPar • edited Mar 1, 2017 by Mitch
11

For what it's worth, System.Exception.HResult is no longer protected in .NET 4.5 -- only the setter is protected. That doesn't help with code that might be compiled with more than one version of the framework.

answered on Stack Overflow May 3, 2013 by UweBaemayr
4

You can also use the ISerializable interface:

static class IOExceptionExtensions
{
    public static int GetHResult(this IOException ex)
    {
        var info = new SerializationInfo(typeof (IOException), new FormatterConverter());
        ex.GetObjectData(info, new StreamingContext());
        return info.GetInt32("HResult");
    }
}
answered on Stack Overflow Nov 19, 2015 by Maxence • edited Nov 20, 2015 by Maxence
0

Does CanRead property help in this case?
i.e. call CanRead, if that returns true, call Read()

answered on Stack Overflow Jun 13, 2009 by shahkalpesh
0

Have you profiled either of these cases? I'd imagine that the reflection method isn't all that slow, especially relative to all the other works your app will be doing and how often this exception will be likely to occur.

If it turns out to be a bottleneck you can look into caching some of the reflection operations or generate dynamic IL to retrieve the property.

answered on Stack Overflow Jun 13, 2009 by Kevin Pullin
0

Necromancing.
Or you can just fetch the protected property by reflection:

private static int GetHresult(System.Exception exception)
{
    int retValue = -666;

    try
    {
        System.Reflection.PropertyInfo piHR = typeof(System.Exception).GetProperty("HResult", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);

        if (piHR != null)
        {
            object o = piHR.GetValue(exception, null);
            retValue = System.Convert.ToInt32(o);
        }
    }
    catch (Exception ex)
    {
    }

    return retValue;
}
answered on Stack Overflow Oct 25, 2019 by Stefan Steiger • edited Oct 28, 2019 by Stefan Steiger

User contributions licensed under CC BY-SA 3.0