Как проверить, не является ли IOException типом Not-Enough-Disk-Space-Exception?

Как проверить, является ли IOException типом исключения "Недостаточно места на диске"?

В настоящий момент я проверяю, соответствует ли сообщение чем-то вроде "Недостаточно места на диске", но я знаю, что это не будет работать, если язык ОС не является английским.

Ответ 1

Вам нужно проверить HResult и протестировать ERROR_DISK_FULL (0x70) и ERROR_HANDLE_DISK_FULL (0x27), который может быть преобразован в HResults на OR 'ing с помощью 0x80070000.

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

static bool IsDiskFull(Exception ex)
{
    const int HR_ERROR_HANDLE_DISK_FULL = unchecked((int)0x80070027);
    const int HR_ERROR_DISK_FULL = unchecked((int)0x80070070);

    return ex.HResult == HR_ERROR_HANDLE_DISK_FULL 
        || ex.HResult == HR_ERROR_DISK_FULL;
}

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

static bool IsDiskFull(Exception ex)
{
    const int ERROR_HANDLE_DISK_FULL = 0x27;
    const int ERROR_DISK_FULL = 0x70;

    int win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
    return win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL;
}

Из документации MSDN:

Обратите внимание, что метод GetHRForException устанавливает IErrorInfoтекущая нить. Это может привести к неожиданным результатам для таких методов, как ThrowExceptionForHR, которые по умолчанию используют IErrorInfoтекущий поток, если он установлен.

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

Ответ 2

В .NET 4.5 свойство getter HResult теперь открыто, поэтому вам больше не нужно использовать Marshal.GetHRForException (вместе с его побочными эффектами).

http://msdn.microsoft.com/en-us/library/system.exception.hresult(v=vs.110).aspx утверждает: "Начиная с .NET Framework 4.5, средство определения свойств HResult защищено, тогда как его получатель является общедоступным. версии .NET Framework, как геттер, так и сеттер защищены"

Итак, вы можете использовать ответ Джастина, но замените Marshal.GetHRForException(ex) на ex.HResult.

Ответ 3

Ну, это немного взломано, но здесь мы идем.

Первое, что нужно сделать, - это получить HResult из исключения. Поскольку это защищенный член, нам нужно немного отразить, чтобы получить значение. Здесь метод расширения будет делать трюк:

public static class ExceptionExtensions
{
    public static int HResultPublic(this Exception exception)
    {
        var hResult = exception.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(z => z.Name.Equals("HResult")).First();
        return (int)hResult.GetValue(exception, null);
    }
}

Теперь, в области захвата, вы можете получить HResult:

catch (Exception ex)
{
    int hResult = ex.HResultPublic();
}

Отсюда вам придется интерпретировать HResult. Вам понадобится эта ссылка.

Нам нужно получить ErrorCode, который хранится в 16 первых битах значения, так что здесь некоторая операция бит:

int errorCode = (int)(hResult & 0x0000FFFF);

Теперь обратитесь к списку кодов системных ошибок, и вот мы:

ERROR_DISK_FULL
112 (0x70)

Итак, протестируйте его, используя:

switch (errorCode)
{
    case 112:
        // Disk full
}

Возможно, есть некоторые функции "более высокого уровня", чтобы получить все это, но по крайней мере он работает.

Ответ 4

Самое простое встроенное решение (мин .NET 4.5 и С# 6):

try
{
    //...
}
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
{
    //...
}

Ответ 5

System.IOException имеет ряд производных типов исключений, однако ни один из этих производных типов не звучит как ваше исключение. Вы можете посмотреть свойство HResult или Data для исключения, возможно, это будет содержать более подробный код ошибки. Согласно MSDN оба эти свойства являются частью этого типа исключения. Просто убедитесь, что вы пытаетесь поймать конкретный тип исключения, а не только базовый тип исключения.