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:
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?
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);
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.
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");
}
}
Does CanRead
property help in this case?
i.e. call CanRead
, if that returns true, call Read()
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.
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;
}
User contributions licensed under CC BY-SA 3.0