
Перехват и блокировка завершения работы Windows

Перехват и блокировка завершения работы Windows
В статье про отключение перезагрузки при установке обновлений, я упомянул программу ShutdownGuard, которая не дает перезагружаться компьютеру. Мне стало очень интересно, каким образом можно из своего приложения перехватить и заблокировать перезагрузку или выключение компьютера, а также каким образом система завершает работу. Результаты исследований вы можете прочитать в этой статье.
Сперва система перебирает все процессы в сеансе активного пользователя. Очередность перебора процессов определяется параметрами выключения, которые можно установить при помощи функции SetProcessShutdownParameters. Если у процесса имеется top-level окно, то ему посылается сообщение WM_QUERYENDSESSION. Именно на этом этапе приложение может заблокировать завершение работы системы, для этого окно должно вернуть FALSE. Опционально приложение может с помощью функции ShutdownBlockReasonCreate установить текстовую строку, которая будет сообщать пользователю причину блокировки. В обработчике окна это выглядит примерно так:
Code (Assembler) : Убрать нумерацию
- ; Юникодная строка сообщения о причине блокировки
- wString du 'Shutdown blocked!',0
- ; Обработка сообщения WM_QUERYENDSESSION
- cmp [msg],WM_QUERYENDSESSION
- je .wm_queryendsession
- ...
- .wm_queryendsession:
- ; Установить строку блокировки и заблокировать завершение работы
- invoke ShutdownBlockReasonCreate,[hwnddlg],wString
- invoke SetWindowLong,[hwnddlg],DWL_MSGRESULT,FALSE
- ...
Если хоть одно приложение заблокировало завершение работы, система останавливает процесс завершения работы, выводит пользователю список блокирующих приложений и начинает отсылать им сообщение WM_ENDSESSION с указанием завершить свою работу. Если, по мнению приложения, работу системы завершать все равно нельзя, оно должно на это сообщение точно так же отвечать FALSE. Время для ответа ограничено 5 секундами, иначе приложение будет принудительно завершено.
Code (Assembler) : Убрать нумерацию
- ; Обработка сообщения WM_ENDSESSION
- cmp [msg],WM_ENDSESSION
- je .wm_endsession
- ...
- .wm_endsession:
- ; Заблокировать завершение работы
- invoke SetWindowLong,[hwnddlg],DWL_MSGRESULT,FALSE
- ...
Code (Assembler) : Убрать нумерацию
- ; Обработка сообщения WM_ENDSESSION
- cmp [msg],WM_ENDSESSION
- je .wm_endsession
- ...
- .wm_endsession:
- ; Удалить строку блокировки и разрешить завершение работы
- invoke ShutdownBlockReasonDestroy,[hwnddlg]
- invoke SetWindowLong,[hwnddlg],DWL_MSGRESULT,TRUE
- ...
В официальной документации приведены особенности работы приложений при нормальном завершении работы системы и так называемом "критическом", когда пользователь нажал кнопку "Принудительное завершение работы" в окне со списком блокирующих приложений.
Нормальное завершение работы | ||
---|---|---|
Сообщение | Приложение | |
Приложение имеет видимое top-level окно или установило строку с причиной блокировки | У приложения нет видимых окон и строка блокировки не установлена | |
WM_QUERYENDSESSION | У приложения есть неограниченное время, чтобы ответить на сообщение WM_QUERYENDSESSION. Windows через 5 секунд открывает окно со списком приложений, блокирующих завершение работы. | У приложения есть 5 секунд, чтобы ответить на сообщение WM_QUERYENDSESSION, иначе Windows принудительно завершит его работу. |
WM_QUERYENDSESSION | Windows отображает окно со списком блокирующих приложений, если приложение ответило FALSE на предыдущее сообщение WM_QUERYENDSESSION. | Windows отправляет сообщение WM_ENDSESSION приложению, если оно ответило FALSE на предыдущее сообщение WM_QUERYENDSESSION. |
WM_ENDSESSION | У приложения есть неограниченное время, чтобы ответить на сообщение WM_ENDSESSION. Windows через 5 секунд открывает окно со списком приложений, блокирующих завершение работы. | У приложения есть 5 секунд, чтобы ответить на сообщение WM_ENDSESSION, иначе Windows принудительно завершит его работу. |
Критическое завершение работы | ||
---|---|---|
Сообщение | Приложение | |
Приложение имеет видимое top-level окно или установило строку с причиной блокировки | У приложения нет видимых окон и строка блокировки не установлена | |
WM_QUERYENDSESSION | У приложения есть 1 секунда, чтобы ответить на сообщение WM_QUERYENDSESSION, иначе Windows принудительно завершит его работу. | У приложения есть 1 секунда, чтобы ответить на сообщение WM_QUERYENDSESSION, иначе Windows принудительно завершит его работу. |
WM_QUERYENDSESSION | Windows отправляет сообщение WM_ENDSESSION приложению, если оно ответило FALSE на предыдущее сообщение WM_QUERYENDSESSION. | Windows отправляет сообщение WM_ENDSESSION приложению, если оно ответило FALSE на предыдущее сообщение WM_QUERYENDSESSION. |
WM_ENDSESSION | У приложения есть 30 секунд, чтобы ответить на сообщение WM_ENDSESSION, иначе Windows принудительно завершит его работу. | У приложения есть 5 секунд, чтобы ответить на сообщение WM_ENDSESSION, иначе Windows принудительно завершит его работу. |
Для отладки вашего приложения на предмет обработки завершения работы системы, лучше всего воспользоваться программами для работы с окнами приложений, но только теми, которые умеют отсылать сообщения окнам. Лично мне больше всего понравилась SendMessage.
Консольные приложения тоже могут обрабатывать и блокировать перезагрузку и завершение работы, но тут есть свои особенности. На Windows XP можно воспользоваться функцией SetConsoleCtrlHandler. У нее широкие возможности, и одна из них - обработка завершения работы. В общих чертах обработка выполняется следующим образом: при старте консольной программы устанавливается обработчик событий, а при завершении работы - снимается.
Code (Assembler) : Убрать нумерацию
- ; Установить обработчик событий
- invoke SetConsoleCtrlHandler,HandlerRoutine,TRUE
- ...
- ...
- ...
- ; Удалить обработчик событий
- invoke SetConsoleCtrlHandler,NULL,FALSE
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------
- ; Обработчик событий консоли
- ;------------------------------------------------------
- proc HandlerRoutine dwCtrlType:DWORD
- ; Завершение работы системы
- cmp [dwCtrlType],CTRL_LOGOFF_EVENT
- jne .loc_ret
- ; Функция обработала событие
- mov eax,TRUE
- ret
- .loc_ret:
- ; Пропустить событие дальше
- mov eax,FALSE
- ret
- endp
Повторюсь, что описанный выше алгоритм действий для консольных программ будет работать только на системе Windows XP. На Windows 7 и выше консольное приложение должно создать невидимое окно и в нем обрабатывать завершение работы точно так же, как и обычное оконное приложение.
Для того, чтобы ваше приложение получило оповещение о завершении работы в самую первую очередь, надо воспользоваться функцией SetProcessShutdownParameters. Вызвав ее с параметром в диапазоне 0x300-0x3FF, вы сообщите системе, что сообщение о завершении работы вашему приложению нужно послать раньше всех. Очередность работает как для оконных приложений, так и для консольных. К сожалению, я не нашел информации, какая очередность оповещения будет в случае, если таких приоритетных приложений несколько.
В приложении примеры консольной и оконной программы с исходными текстами, которые перехватывают и блокируют завершение работы системы. Оконная программа при этом устанавливает строку блокировки, а консольная может корректно работать только на системе Windows XP.
Просмотров: 5343 | Комментариев: 3

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(24.03.2023 в 10:59):
Так бывает. Поправил.

Аудитор приложений
(24.03.2023 в 04:45):
В приложении пример только оконной программы, но нет примера консольной.

ManHunter
(08.04.2018 в 19:04):
Дополнил статью информацией про консольные приложения и про установку очередности при помощи функции SetProcessShutdownParameters.

Добавить комментарий
Заполните форму для добавления комментария
