2009-06-13 14 views
33

La proprietà System.Exception.HResult è protetta. Come posso sbirciare all'interno di un'eccezione e ottenere l'HResult senza ricorrere a riflessioni o altri brutti hack?Come si determina HResult per System.IO.IOException?


Ecco la situazione:
Voglio scrivere uno strumento di backup, che si apre e legge i file su un sistema. Apro il file con FileAccess.Read e FileShare.ReadWrite, secondo this guidance, perché non mi interessa se il file è aperto per la scrittura al momento in cui l'ho letto.

In alcuni casi, quando un file che sto leggendo è aperto da un'altra app, il metodo System.IO.FileStream.Read() genera un System.IO.IOException, "Il processo non può accedere al file perché un altro processo ha bloccato una parte del file ". Questo è error 33 o penso HResult 0x80070021. [EDIT :. Credo che questo può essere restituito quando un altro processo chiama LockFileEx per bloccare un intervallo di byte all'interno di un file]

mi piacerebbe mettere in pausa e riprovare quando ottengo questo errore. Penso che questa sia l'azione appropriata da portare qui. Se il processo di blocco rilascia rapidamente il blocco dell'intervallo di byte, posso continuare a leggere il file.

Come posso distinguere una IOException per questo motivo, da altri? Posso pensare a questi modi:

  • riflessione privata - non voglio farlo. La perfezione puzzerà.
  • chiama Exception.ToString() e analizza la stringa. Sembra hacky. Non funzionerà nelle versioni i18n.

Non mi piacciono queste opzioni. Non c'è un modo migliore, più pulito?


Ho appena cercato in giro e ho trovato System.Runtime.InteropServices.Marshal.GetHRForException. Ciò restituirà un uint come 0x80070021?

+3

> riflessione privata - voglio non farlo. La perfezione puzzerà. - L'eccezione perf puzza comunque, quindi non mi preoccuperei dell'aspetto perf. La riflessione, tuttavia, richiede FullTrust, è brutta, non è supportata e soggetta a rotture - ecco perché non dovresti farlo. –

risposta

54

Per .Net Framework 4.5 e versioni successive, è possibile utilizzare la proprietà Exception.HResult:

int hr = ex.HResult; 

Per le versioni precedenti, è possibile utilizzare Marshal.GetHRForException per tornare alla HResult, ma questo has significant side-effects and is not recommended:

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

Grazie mille! Questo facilita lo sviluppo in caso di interoperabilità C#/COM. – rds

+2

+1 Eww, richiede piena fiducia ... Ma è comunque una soluzione. – reSPAWNed

+3

Attenzione agli effetti collaterali: "Si noti che il metodo ** GetHRForException ** imposta ** IErrorInfo ** del thread corrente. Ciò può causare risultati imprevisti per metodi come i metodi ** ThrowExceptionForHR ** che utilizzano in modo predefinito il ** IErrorInfo ** del thread corrente se è impostato. " – HugoRune

0

L'aiuto di proprietà CanRead in questo caso?
cioè chiamano CanRead, se questo restituisce true, chiamare Read()

+0

No, CanRead è true. Credo che l'80070021 sia un errore transitorio. Se sto leggendo il documento correttamente, per gestirlo la pratica raccomandata è "aspettare un po 'e riprovare". – Cheeso

+0

E 'possibile che tu abbia aperto il file per leggerlo e qualcun altro l'abbia aperto (usando FileShare.Read), il primo chiamante non può leggerlo più? È questo che intendi per transitorio? – shahkalpesh

+0

No, ciò che intendo è che un altro processo ha chiamato FileLock o FileLockEx (http://msdn.microsoft.com/en-us/library/aa365203.aspx) sul file per bloccare un intervallo all'interno del file. Questo è talvolta chiamato un blocco dell'intervallo di byte. Ad un certo punto il processo di blocco rilascerà il blocco della distanza. Questo è ciò che intendo per "Transitorio". – Cheeso

0

Avete profilato uno di questi casi? Immagino che il metodo di riflessione non sia poi così lento, soprattutto rispetto a tutti gli altri lavori che la tua app farà e alla frequenza con cui è probabile che si verifichi questa eccezione.

Se risulta essere un collo di bottiglia, è possibile esaminare la memorizzazione nella cache di alcune delle operazioni di riflessione o generare IL dinamico per recuperare la proprietà.

11

Per quello che vale, System.Exception.HResult non è più protetto in .NET 4.5 - solo il setter è protetto. Ciò non aiuta con il codice che potrebbe essere compilato con più di una versione del framework.

3

È inoltre possibile utilizzare l'interfaccia 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"); 
    } 
} 
Problemi correlati