views:

402

answers:

3

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?

A: 

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

shahkalpesh
Nope, CanRead is true. I believe 80070021 is a transient error. If I am reading the doc correctly, to handle it the recommended practice is to "wait a while and retry."
Cheeso
shahkalpesh
No, what I mean is, another process has called FileLock or FileLockEx (http://msdn.microsoft.com/en-us/library/aa365203.aspx) on the file to lock a range within the file. This is sometimes called a byte-range lock. At some point the locking process will release the range lock. That is what I mean by "Transient."
Cheeso
Thanks Cheeso. Coming from a VB background, I thought that file can be locked completely by 1 reader. Never thought that a range of characters can be locked for reading.
shahkalpesh
A: 

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.

Kevin Pullin
+5  A: 

You can use Marshal.GetHRForException to get back the HResult:

int hr = Marshal.GetHRForException( ex );

Reference

JaredPar