¿Cómo determino HResult para System.IO.IOException?

La propiedad System.Exception.HResult está protegida. ¿Cómo puedo ver dentro de una excepción y obtener el resultado HR sin recurrir a la reflexión u otros hacks feos?


Aquí está la situación:
Quiero escribir una herramienta de copia de seguridad, que abre y lee archivos en un sistema. Abrí el archivo con FileAccess.Read y FileShare.ReadWrite, de acuerdo con esta guía , porque no me importa si el archivo está abierto para la escritura en el momento en que lo leí.

En algunos casos, cuando un archivo que estoy leyendo es abierto por otra aplicación, el método System.IO.FileStream.Read () arroja System.IO.IOException, “El proceso no puede acceder al archivo porque otro proceso ha bloqueado una parte de el archivo”. Este es el error 33 , o creo HResult 0x80070021. [ EDITAR : creo que se puede devolver cuando otro proceso llama a LockFileEx para bloquear un rango de bytes dentro de un archivo.]

Me gustaría hacer una pausa y volver a intentar cuando recibo este error. Creo que esta es la acción adecuada para tomar aquí. Si el proceso de locking libera el locking de rango de bytes rápidamente, entonces puedo continuar leyendo el archivo.

¿Cómo puedo distinguir una IOException por este motivo, de otros? Puedo pensar de estas maneras:

  • Reflexión privada: no quiero hacer eso. Perf apestará.
  • llama a Exception.ToString () y analiza la cadena. Se siente hacky. No funcionará en las versiones i18n.

No me gustan estas opciones. ¿No hay una manera mejor y más limpia?


Acabo de buscar y encontré System.Runtime.InteropServices.Marshal.GetHRForException . ¿Eso devolverá un uint como 0x80070021?

Para .Net Framework 4.5 y superior, puede usar la propiedad Exception.HResult :

 int hr = ex.HResult; 

Para versiones anteriores, puede usar Marshal.GetHRForException para recuperar HResult, pero esto tiene importantes efectos secundarios y no se recomienda :

 int hr = Marshal.GetHRForException(ex); 

Por lo que vale, System.Exception.HResult ya no está protegido en .NET 4.5, solo el setter está protegido. Eso no ayuda con el código que podría comstackrse con más de una versión del marco.

También puede usar la interfaz ISerializable :

 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"); } } 

¿ CanRead propiedad CanRead en este caso?
es decir, llame a CanRead , si eso devuelve verdadero, llame a Read()

¿Ha perfilado alguno de estos casos? Me imagino que el método de reflexión no es tan lento, especialmente en relación con todos los otros trabajos que su aplicación hará y con qué frecuencia es probable que ocurra esta excepción.

Si resulta ser un cuello de botella, puede buscar en caché algunas de las operaciones de reflexión o generar IL dynamic para recuperar la propiedad.