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

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

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


person jotbek    schedule 15.02.2012    source источник
comment
Почему вашему коду не все равно, в чем причина исключения IOException?   -  person CodesInChaos    schedule 15.02.2012
comment
Мне нужно сообщить пользователю, когда что-то не работает, и что это причина, и он должен очистить место на диске. У меня есть внешние зависимости, которые работают с файловой системой и получаю от них такое исключение.   -  person jotbek    schedule 15.02.2012
comment
Сообщение об исключении уже сообщает пользователю, в чем причина.   -  person CodesInChaos    schedule 15.02.2012
comment
Но если вы разрабатываете приложение на английском языке, вы не можете отображать сообщение на языке ОС (например, на немецком), если это не-английская операционная система.   -  person jotbek    schedule 15.02.2012


Ответы (5)


Вам необходимо проверить HResult и протестировать против ERROR_DISK_FULL (0x70) и ERROR_HANDLE_DISK_FULL (0x27), который может преобразовать в HResults OR с помощью 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;
}

Для более старых версий вы можете использовать _7 _, чтобы вернуть результат 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?

person Justin    schedule 15.02.2012
comment
Выглядит немного чище, чем решение Ганса. - person CodesInChaos; 15.02.2012
comment
С другой стороны, Marshal.GetHRForException иметь побочный эффект крайне уродливо. - person CodesInChaos; 15.02.2012
comment
Я думаю, что это самая чистая душа, которую вы можете найти. Я подожду еще немного, но думаю, это отметит это как ответ. :) Спасибо! - person jotbek; 15.02.2012
comment
@Justin: Не должно быть & вместо оператора &&? - person jotbek; 15.02.2012
comment
@CodeInChaos Хорошо заметил, я сам этого не заметил и поэтому добавил примечание к ответу. @ jotbek Упс! Я исправил это, но, очевидно, это не вошло в ту версию, которую я опубликовал! - person Justin; 15.02.2012
comment
Начиная с .Net-Framework 4.5 геттер для Exception.HResult больше не защищен. Я думаю, это могло бы заменить вызов GetHRForException и избавиться от упомянутого побочного эффекта. - person mklein; 19.06.2015
comment
@Justin Это уже не лучший ответ, если вы ориентируетесь на 4.5 или выше. - person jnm2; 22.12.2015
comment
Я настоятельно рекомендую вам не использовать GetHRForException. Это вызвало очень странную проблему, на понимание которой у нас потребовались месяцы: stackoverflow.com/a/40242031/5844190 - person Alsty; 25.10.2016
comment
Теперь есть более полная страница документации по HRESULT по адресу docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/. В нем есть все коды Win32, преобразованные в HRESULT (т.е. с функцией 7 в старшем слове), а также коды результатов от других средств. В том же разделе документации есть даже страница, объясняющая битовое расположение значения HRESULT. - person Peter Duniho; 13.03.2019

В .NET 4.5 геттер свойства 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.

person BateTech    schedule 31.03.2014

Что ж, это немного взломано, но поехали.

Первым делом нужно получить 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
}

Может быть, есть какие-то функции «более высокого уровня», чтобы получить все это, но, по крайней мере, это работает.

person ken2k    schedule 15.02.2012
comment
+1, поскольку на самом деле (начиная с .NET 4.5) это больше не это беспорядок, потому что HResult был повышен public. Осталось только возможно кэшировать вызов GetProperties в статическом члене - это сделало бы красивую оболочку с обратно совместимой оболочкой (при условии, что вы используете оба флага привязки Public и NonPublic - что вы и сделали :-)). Хотя перед лицом обработки исключений увеличение производительности кешируемого элемента, вероятно, является спорным. - person Christian.K; 22.12.2015
comment
Чтобы сделать его идеально чистым, я привожу Exception.HResult к своему HResultErrorCodes перечислению на основе ushort, которое единолично применяет маску 0xFFFF и устраняет необходимость в магических числах или константах в вызывающем коде. - person jnm2; 22.12.2015

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

try
{
    //...
}
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
{
    //...
}
person Dominik Palo    schedule 15.04.2016

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

person AaronHS    schedule 15.02.2012
comment
Чтобы использовать HResult, мне нужно было бы использовать отражение, как здесь: dotnetspider.com/ forum / 101158-Disk-full-C.aspx может быть лучше, чем использовать сообщение, но все же хак. Я не уверен, где я могу найти некоторую спецификацию того, что в этом случае находится в общедоступном свойстве данных. - person jotbek; 15.02.2012
comment
правда, не понимал, что это защищено. - person AaronHS; 16.02.2012