C#, Об анализе exceptions при вызове внешнего процесса.

Или отлавливаем нажатие клавиши «Отмена» в окне запроса UAC.

Вот однажды я писал небольшую утилиту, которая запускает любое приложение или командный файл (bat/cmd) от имени администратора. И мне в комментариях правильно намекнули, что я слишком грубо обрабатываю exceptions, которые могут случиться во время запуска внешнего процесса. Но вообще это хороший пример не только для конкретного случая, но и для подхода к обработке ошибок вообще. Кратко говоря — если вы предполагаете, что где-то может возникнуть ошибка, то есть два метода:
1. Предотвратить и обезвредить. К таким ошибкам, например, относится возможная недоступность файла для чтения/записи, или вообще его отсутствие, когда он нужен. Тогда лучше проверить, например, наличие файла, с помощью File.Exist() перед операцией с файлом.
2. Отловить на этапе времени выполнения. Для этого в C# существуют try/catch.

Нам нужен именно способ #2, поскольку мы не знаем и проверить заранее никак не можем, нажмет пользователь «Отмену» в окне запроса, или нет.

Изначально было сделано так, т.е. тут мы полагались на какой-то внутренний флаг, и от его состояния принимали решение, реагировать на ошибку или нет.

3. Но на самом деле нам нужно отследить конкретную ошибку, для соответствующей нашему случаю реакции на нее, а изначально, мы этого не сделали, полагаясь на авось (внутренний флаг).

Немного теории об exceptions при запуске внешних процессов.

Как известно, в .NET ошибки времени выполнения распределены по классам. Есть общий класс — Exception, в который попадают все ошибки времени выполнения, и есть конкретные классы для обработки определенных ошибок. Иерархия обработки следующая: «от конкретных к общему». Т.е. сначала (если мы хотим их обработать), указываются конкретные ошибки, а потом можно, но не обязательно указать общий обработчик.
Конкретно при запуске внешних процессов могут возникнуть следующие виды ошибок:
ArgumentNullException
ObjectDisposedException
FileNotFoundException
(на самом деле в зависимости от OS но может не сработать, сработает следующий)
Win32Exception
он нам и нужен
PlatformNotSupportedException

На самом деле, практически всегда срабатывает класс ошибок Win32Exception, остальные или очень редки, или их можно охарактеризовать одним словом — случился ой, дальше работать не будем.

Анализ Win32Exception

Для начала подключим нужный namespace:
using System.ComponentModel;

А теперь примемся за анализ. На самом деле, у каждой Win32-ошибки имеется внутренний код. В C#-exceptions он сохраняется в переменной NativeErrorCode. Т.е для решения нашей задачи, нам в конструкции try/catch достаточно отловить конкретный код ошибки. Для нажатия клавиши «Отмена» в окне UAC, это будет код 1223, «Операция отменена пользователем».

Исправление.

1. Сначала надо отловить ошибку типа Win32Exception и проанализировать значение NativeErrorCode.
2. Если NativeErrorCode == 1223, то не предпринимаем никаких действий.
3. Если NativeErrorCode другой, оповещаем пользователя об ошибке.
4. Если сработало исключение другого типа (не Win32Exception), то аналогично предыдущему пункту — оповещаем пользователя об ошибке.

//...

try
{
    Process.Start(psi);
}
catch (Win32Exception wex)
{
    if (wex.NativeErrorCode == 1223) //нажали "Отмену" в окне UAC
    {
        return true;
    }
    else //какой-то другой Win32 Error
    {
        ErrorMessage = wex.NativeErrorCode.ToString() + " " + wex.Message;
        return false;
    }
}
catch (Exception ex) //какой-то другой Exception
{
    ErrorMessage = ex.Message;
    return false;
}

//...

Код полностью здесь

Источники

1. Коды ошибок Win32 (Краткое пояснение и полный список кодов, англ., MSDN)
2. Нужный код ошибки (Отменено пользователем)

Утилита wsudo

1. Репозиторий на GitHub
2. Скачать
3. Заметка об утилите Копия

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *