Или отлавливаем нажатие клавиши «Отмена» в окне запроса UAC.
Вот однажды я писал небольшую утилиту, которая запускает любое приложение или командный файл (bat/cmd) от имени администратора. И мне в комментариях правильно намекнули, что я слишком грубо обрабатываю exceptions, которые могут случиться во время запуска внешнего процесса. Но вообще это хороший пример не только для конкретного случая, но и для подхода к обработке ошибок вообще. Кратко говоря — если вы предполагаете, что где-то может возникнуть ошибка, то есть два метода:
1. Предотвратить и обезвредить. К таким ошибкам, например, относится возможная недоступность файла для чтения/записи, или вообще его отсутствие, когда он нужен. Тогда лучше проверить, например, наличие файла, с помощью File.Exist()
перед операцией с файлом.
2. Отловить на этапе времени выполнения. Для этого в C# существуют try/catch
.
Нам нужен именно способ #2, поскольку мы не знаем и проверить заранее никак не можем, нажмет пользователь «Отмену» в окне запроса, или нет.
Изначально было сделано так, т.е. тут мы полагались на какой-то внутренний флаг, и от его состояния принимали решение, реагировать на ошибку или нет.
3. Но на самом деле нам нужно отследить конкретную ошибку, для соответствующей нашему случаю реакции на нее, а изначально, мы этого не сделали, полагаясь на авось (внутренний флаг).
Как известно, в .NET ошибки времени выполнения распределены по классам. Есть общий класс — Exception
, в который попадают все ошибки времени выполнения, и есть конкретные классы для обработки определенных ошибок. Иерархия обработки следующая: «от конкретных к общему». Т.е. сначала (если мы хотим их обработать), указываются конкретные ошибки, а потом можно, но не обязательно указать общий обработчик.
Конкретно при запуске внешних процессов могут возникнуть следующие виды ошибок:
ArgumentNullException
(на самом деле в зависимости от OS но может не сработать, сработает следующий)
ObjectDisposedException
FileNotFoundException
— он нам и нужен
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. Нужный код ошибки (Отменено пользователем)
1. Репозиторий на GitHub
2. Скачать
3. Заметка об утилите Копия