2009-06-13 24 views
33

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?¿Cómo determino HResult para System.IO.IOException?


Esta es la situación:
Quiero escribir una herramienta de copia de seguridad, que se abre y lee los archivos en un sistema. Abrí el archivo con FileAccess.Read y FileShare.ReadWrite, de acuerdo con this guidance, 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 está abierto por otra aplicación, el método System.IO.FileStream.Read() arroja una excepción System.IO.IOException, "El proceso no puede acceder al archivo porque otro proceso tiene bloqueó una parte del archivo ". Esto es error 33, o creo HResult 0x80070021. [EDITAR :. Creo que esto puede ser devuelto cuando otro proceso llama LockFileEx para bloquear un rango de bytes dentro de un archivo]

me gustaría hacer una pausa y volver a intentar cuando me sale este error. Creo que esta es la acción adecuada para tomar aquí. Si el proceso de bloqueo libera el bloqueo 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?

+3

> reflexión privada - no quiero hacer eso. Perf apestará. - La perfusión de excepciones huele de todos modos, así que no me preocuparía el aspecto del rendimiento. La reflexión, sin embargo, requiere FullTrust, es fea y no es compatible y es propensa a la rotura, por lo que no deberías hacerlo. –

Respuesta

54

Para .Net Framework 4.5 y superiores, se puede utilizar la propiedad Exception.HResult:

int hr = ex.HResult; 

Para versiones anteriores, puede utilizar Marshal.GetHRForException para recuperar el HResult, pero esto has significant side-effects and is not recommended:

int hr = Marshal.GetHRForException(ex); 
+0

¡Muchas gracias! Esto realmente facilita el desarrollo en caso de interoperabilidad C#/COM. – rds

+2

+1 Eww, requiere plena confianza ... Pero es una solución, no obstante. – reSPAWNed

+3

Tenga cuidado con los efectos secundarios: "Tenga en cuenta que el método ** GetHRForException ** establece el ** IErrorInfo ** del hilo actual. Esto puede causar resultados inesperados para métodos como los métodos ** ThrowExceptionForHR ** que usan de manera predeterminada el ** IErrorInfo ** del hilo actual si está configurado. " – HugoRune

0

¿Ayuda la propiedad CanRead en este caso?
decir llaman CanRead, si es que devuelve true, llame Read()

+0

No, CanRead es cierto. Creo que 80070021 es un error transitorio. Si estoy leyendo el documento correctamente, para manejarlo, la práctica recomendada es "esperar un momento y volver a intentarlo". – Cheeso

+0

¿Es posible que haya abierto el archivo para leer y que otra persona lo haya abierto también (utilizando FileShare.Read), la 1ra persona que llama ya no puede leerlo? ¿Es eso lo que quieres decir con transitorio? – shahkalpesh

+0

No, lo que quiero decir es que otro proceso ha llamado FileLock o FileLockEx (http://msdn.microsoft.com/en-us/library/aa365203.aspx) en el archivo para bloquear un rango dentro del archivo. Esto a veces se llama bloqueo de rango de bytes. En algún momento, el proceso de bloqueo liberará el bloqueo de rango. Eso es lo que quiero decir con "transitorio". – Cheeso

0

¿Ha perfilado cualquiera 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 se trata de un cuello de botella, puede buscar en el caché algunas de las operaciones de reflexión o generar IL dinámico para recuperar la propiedad.

11

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 compilarse con más de una versión del marco.

3

También puede utilizar 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"); 
    } 
} 
Cuestiones relacionadas