Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Перехват и блокировка завершения работы Windows
Перехват и блокировка завершения работы Windows

В статье про отключение перезагрузки при установке обновлений, я упомянул программу ShutdownGuard, которая не дает перезагружаться компьютеру. Мне стало очень интересно, каким образом можно из своего приложения перехватить и заблокировать перезагрузку или выключение компьютера, а также каким образом система завершает работу. Результаты исследований вы можете прочитать в этой статье.

Сперва система перебирает все процессы в сеансе активного пользователя. Очередность перебора процессов определяется параметрами выключения, которые можно установить при помощи функции SetProcessShutdownParameters. Если у процесса имеется top-level окно, то ему посылается сообщение WM_QUERYENDSESSION. Именно на этом этапе приложение может заблокировать завершение работы системы, для этого окно должно вернуть FALSE. Опционально приложение может с помощью функции ShutdownBlockReasonCreate установить текстовую строку, которая будет сообщать пользователю причину блокировки. В обработчике окна это выглядит примерно так:
  1. ; Юникодная строка сообщения о причине блокировки
  2. wString du 'Shutdown blocked!',0
  3.  
  4.         ; Обработка сообщения WM_QUERYENDSESSION
  5.         cmp     [msg],WM_QUERYENDSESSION
  6.         je      .wm_queryendsession
  7.         ...
  8. .wm_queryendsession:
  9.         ; Установить строку блокировки и заблокировать завершение работы
  10.         invoke  ShutdownBlockReasonCreate,[hwnddlg],wString
  11.         invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,FALSE
  12.         ...
Важные замечания. Приложение должно ответить на сообщение WM_QUERYENDSESSION в течение 5 секунд, иначе система принудительно завершит его. Строка блокировки обязательно должна быть в юникоде.

Если хоть одно приложение заблокировало завершение работы, система останавливает процесс завершения работы, выводит пользователю список блокирующих приложений и начинает отсылать им сообщение WM_ENDSESSION с указанием завершить свою работу. Если, по мнению приложения, работу системы завершать все равно нельзя, оно должно на это сообщение точно так же отвечать FALSE. Время для ответа ограничено 5 секундами, иначе приложение будет принудительно завершено.
  1.         ; Обработка сообщения WM_ENDSESSION
  2.         cmp     [msg],WM_ENDSESSION
  3.         je      .wm_endsession
  4.         ...
  5. .wm_endsession:
  6.         ; Заблокировать завершение работы
  7.         invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,FALSE
  8.         ...
Если все необходимые операции закончены, приложение должно удалить строку блокировки при помощи функции ShutdownBlockReasonDestroy и вернуть TRUE в ответ на сообщение системы WM_ENDSESSION.
  1.         ; Обработка сообщения WM_ENDSESSION
  2.         cmp     [msg],WM_ENDSESSION
  3.         je      .wm_endsession
  4.         ...
  5. .wm_endsession:
  6.         ; Удалить строку блокировки и разрешить завершение работы
  7.         invoke  ShutdownBlockReasonDestroy,[hwnddlg]
  8.         invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,TRUE
  9.         ...
Если в задачи приложения не входит непосредственно блокировка завершения работы системы, оно должно действовать следующим образом. На сообщение WM_QUERYENDSESSION отвечает FALSE, чтобы не допустить принудительного завершения, но начинает подготовительные работы: закрывает соединение с базой данных, сохраняет конфиги, логи и т.п. Все это время обработчик сообщения WM_ENDSESSION также должен возвращать FALSE. Как только все финальные работы закончены, приложение должно самостоятельно завершиться.

В официальной документации приведены особенности работы приложений при нормальном завершении работы системы и так называемом "критическом", когда пользователь нажал кнопку "Принудительное завершение работы" в окне со списком блокирующих приложений.

Нормальное завершение работы
СообщениеПриложение
Приложение имеет видимое top-level окно или установило строку с причиной блокировкиУ приложения нет видимых окон и строка блокировки не установлена
WM_QUERYENDSESSIONУ приложения есть неограниченное время, чтобы ответить на сообщение WM_QUERYENDSESSION. Windows через 5 секунд открывает окно со списком приложений, блокирующих завершение работы.У приложения есть 5 секунд, чтобы ответить на сообщение WM_QUERYENDSESSION, иначе Windows принудительно завершит его работу.
WM_QUERYENDSESSIONWindows отображает окно со списком блокирующих приложений, если приложение ответило 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_QUERYENDSESSIONWindows отправляет сообщение 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. У нее широкие возможности, и одна из них - обработка завершения работы. В общих чертах обработка выполняется следующим образом: при старте консольной программы устанавливается обработчик событий, а при завершении работы - снимается.
  1.         ; Установить обработчик событий
  2.         invoke  SetConsoleCtrlHandler,HandlerRoutine,TRUE
  3.         ...
  4.         ...
  5.         ...
  6.         ; Удалить обработчик событий
  7.         invoke  SetConsoleCtrlHandler,NULL,FALSE
В пользовательском обработчике событий надо предусмотреть проверку на случай события CTRL_LOGOFF_EVENT. Если завершать работу системы пока нельзя, обработчик события должен вернуть TRUE, а если можно, то FALSE. Да, соглашусь, не самое логичное поведение. Когда приложение блокирует завершение работы системы, она выводит диалоговое окно с предложением принудительного завершения программы. До принятия решения пользователем, никаких повторных попыток завершить приложение не выполняется.
  1. ;------------------------------------------------------
  2. ; Обработчик событий консоли
  3. ;------------------------------------------------------
  4. proc HandlerRoutine dwCtrlType:DWORD
  5.         ; Завершение работы системы
  6.         cmp     [dwCtrlType],CTRL_LOGOFF_EVENT
  7.         jne     .loc_ret
  8.  
  9.         ; Функция обработала событие
  10.         mov     eax,TRUE
  11.         ret
  12. .loc_ret:
  13.         ; Пропустить событие дальше
  14.         mov     eax,FALSE
  15.         ret
  16. endp
Что интересно, если посмотреть документацию, то в описании функции SetConsoleCtrlHandler есть событие CTRL_SHUTDOWN_EVENT, которое, судя по названию, должно больше подходить для обработки завершения работы системы. Но на практике это событие получают только приложения, стартовавшие в виде системных сервисов. "Л" - логика.

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

Для того, чтобы ваше приложение получило оповещение о завершении работы в самую первую очередь, надо воспользоваться функцией SetProcessShutdownParameters. Вызвав ее с параметром в диапазоне 0x300-0x3FF, вы сообщите системе, что сообщение о завершении работы вашему приложению нужно послать раньше всех. Очередность работает как для оконных приложений, так и для консольных. К сожалению, я не нашел информации, какая очередность оповещения будет в случае, если таких приоритетных приложений несколько.

В приложении примеры консольной и оконной программы с исходными текстами, которые перехватывают и блокируют завершение работы системы. Оконная программа при этом устанавливает строку блокировки, а консольная может корректно работать только на системе Windows XP.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

System.Shutdown.Demo.zip (4,441 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 5343 | Комментариев: 3

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (24.03.2023 в 10:59):
Так бывает. Поправил.
Аудитор приложений (24.03.2023 в 04:45):
В приложении пример только оконной программы, но нет примера консольной.
ManHunter (08.04.2018 в 19:04):
Дополнил статью информацией про консольные приложения и про установку очередности при помощи функции SetProcessShutdownParameters.

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0073 сек.) / Память: 4.5 Mb
Наверх