Как определить HResult для System.IO.IOException?

Свойство System.Exception.HResult защищено. Как я могу заглянуть внутрь исключения и получить HResult, не прибегая к рефлексии или другим уродливым приемам?


Вот ситуация:
Я хочу написать инструмент резервного копирования, который открывает и читает файлы в системе. Я открываю файл с помощью FileAccess.Read и FileShare.ReadWrite в соответствии с это руководство, потому что мне все равно, открыт ли файл для записи в то время, когда я его читаю.

В некоторых случаях, когда файл, который я читаю, открыт другим приложением, метод System.IO.FileStream.Read () выдает исключение System.IO.IOException: «Процесс не может получить доступ к файлу, потому что другой процесс заблокировал часть файл". Это ошибка 33, или, я думаю, HResult 0x80070021. [ИЗМЕНИТЬ: я считаю, что это может быть возвращено, когда другой процесс вызывает LockFileEx, чтобы заблокировать диапазон байтов в файле.]

Я хотел бы сделать паузу и повторить попытку, когда получу эту ошибку. Я думаю, что это уместное действие, которое следует предпринять здесь. Если процесс блокировки быстро снимает блокировку диапазона байтов, я могу продолжить чтение файла.

Как я могу отличить IOException по этой причине от других? Я могу думать об этих способах:

  • личное отражение - не хочу этого делать. Перф будет вонять.
  • вызовите Exception.ToString () и проанализируйте строку. Чувствует себя взломанным. Не будет работать в версиях i18n.

Мне эти варианты не нравятся. Нет лучшего и более чистого способа?


Я просто поискал и нашел System.Runtime.InteropServices .Marshal.GetHRForException. Будет ли это возвращать uint вроде 0x80070021?


person Cheeso    schedule 13.06.2009    source источник
comment
›Личное отражение - не хочу этого делать. Перф будет вонять. - Exception perf все равно воняет, так что я бы не стал беспокоиться о производительности. Однако для отражения требуется FullTrust, он уродлив, не поддерживается и подвержен поломкам - вот почему вам не следует этого делать.   -  person Mark Brackett    schedule 08.08.2009


Ответы (6)


Для .Net Framework 4.5 и выше вы можете использовать свойство Exception.HResult:

int hr = ex.HResult;

Для более старых версий вы можете использовать _3 _, чтобы вернуть результат HResult, но это имеет серьезные побочные эффекты и не рекомендуется:

int hr = Marshal.GetHRForException(ex);
person JaredPar    schedule 13.06.2009
comment
Большое спасибо! Это действительно облегчает разработку в случае взаимодействия C # / COM. - person rds; 10.12.2010
comment
+1 Фуу, требует полного доверия ... Но все же решение. - person reSPAWNed; 17.07.2012
comment
Остерегайтесь побочных эффектов: обратите внимание, что метод GetHRForException устанавливает IErrorInfo текущего потока. Это может привести к неожиданным результатам для таких методов, как методы ThrowExceptionForHR, которые по умолчанию используют IErrorInfo текущего потока, если он установлен. - person HugoRune; 17.06.2013
comment
Я настоятельно рекомендую вам не использовать GetHRForException. Это вызвало очень странную проблему, на понимание которой у нас ушли месяцы: stackoverflow.com/a/40242031/5844190 - person Alsty; 25.10.2016

Как бы то ни было, System.Exception.HResult больше не защищен в .NET 4.5 - защищен только установщик. Это не помогает с кодом, который может быть скомпилирован с более чем одной версией фреймворка.

person UweBaemayr    schedule 03.05.2013

Вы также можете использовать интерфейс 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");
    }
}
person Maxence    schedule 19.11.2015

Помогает ли свойство CanRead в этом случае?
т.е. вызовите CanRead, если это вернет истину, вызовите Read()

person shahkalpeshp    schedule 13.06.2009
comment
Нет, CanRead - это правда. Я считаю, что 80070021 - временная ошибка. Если я читаю документ правильно, рекомендуется немного подождать и повторить попытку. - person Cheeso; 14.06.2009
comment
Возможно ли, что вы открыли файл для чтения, и кто-то еще открыл его (используя FileShare.Read), 1-й вызывающий больше не может его читать? Это то, что вы имеете в виду под переходным процессом? - person shahkalpeshp; 14.06.2009
comment
Нет, я имею в виду, что другой процесс вызвал FileLock или FileLockEx (msdn.microsoft .com / en-us / library / aa365203.aspx) в файле, чтобы заблокировать диапазон внутри файла. Иногда это называется блокировкой байтового диапазона. В какой-то момент процесс блокировки снимет блокировку диапазона. Вот что я имею в виду под переходным процессом. - person Cheeso; 14.06.2009
comment
Спасибо, Чизо. Исходя из опыта работы с VB, я подумал, что файл может быть полностью заблокирован одним читателем. Никогда не думал, что ряд символов можно заблокировать для чтения. - person shahkalpeshp; 14.06.2009

Вы профилировали какой-либо из этих случаев? Я полагаю, что метод отражения не так уж и медленен, особенно по сравнению со всеми другими работами, которые ваше приложение будет выполнять, и как часто это исключение может возникать.

Если это окажется узким местом, вы можете изучить кеширование некоторых операций отражения или сгенерировать динамический IL для извлечения свойства.

person Kevin Pullin    schedule 13.06.2009

Некромантинг.
Или вы можете просто получить защищенное свойство с помощью отражения:

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;
}
person Stefan Steiger    schedule 25.10.2019