Blog. Just Blog

Перехват Ctrl+C и Ctrl+Break в консольных программах

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

В отличие от оконных приложений, у консольных программ не так много возможностей по обработке разных нештатных ситуаций, например, внепланового завершения работы. Это может быть нажатие комбинаций Ctrl+С, Ctrl+Break или закрытие окна консоли. Правильно написанное приложение должно уметь обрабатывать подобные ситуации и корректно завершать работу, то есть успеть сохранить какие-то промежуточные данные, освободить занятые ресурсы или уведомить пользователя, что его действия контрпродуктивны.

В WinAPI есть замечательная функция SetConsoleCtrlHandler, с помощью которой консольное приложение может не только узнавать о попытках его принудительного завершения, но даже блокировать нажатия Ctrl+С и Ctrl+Break. Работа с этой функцией очень простая: при старте консольное приложение с ее помощью устанавливает свой обработчик событий, а при завершении с ее же помощью убирает его:
  1.         ; Установить обработчик событий
  2.         invoke  SetConsoleCtrlHandler,HandlerRoutine,TRUE
  3.         ...
  4.         ...
  5.         ...
  6.         ; Удалить обработчик событий
  7.         invoke  SetConsoleCtrlHandler,NULL,FALSE
Сам обработчик выглядит примерно следующим образом. На входе передается единственный параметр - тип события. На выходе должно быть FALSE, если приложение согласно с действиями пользователя, или TRUE, если это событие программа обработала самостоятельно и оно должно быть, по возможности, подавлено.
  1. proc HandlerRoutine dwCtrlType:DWORD
  2.         ; Нажата комбинация Ctrl+C
  3.         cmp     [dwCtrlType],CTRL_C_EVENT
  4.         jne     @f
  5.         ...
  6.         ...
  7.         jmp     .loc_handle
  8. @@:
  9.         ; Нажата комбинация Ctrl+Break
  10.         cmp     [dwCtrlType],CTRL_BREAK_EVENT
  11.         jne     @f
  12.         ...
  13.         ...
  14.         jmp     .loc_handle
  15. @@:
  16.         ; Окно консоли закрывается
  17.         cmp     [dwCtrlType],CTRL_CLOSE_EVENT
  18.         jne     .loc_ret
  19.         ...
  20.         ...
  21.  
  22. .loc_handle:
  23.         ; Функция обработала событие
  24.         mov     eax,TRUE
  25.         ret
  26. .loc_ret:
  27.         ; Пропустить событие дальше
  28.         mov     eax,FALSE
  29.         ret
  30. endp
Стоит отметить, что консольное приложение может подавить только нажатия на Ctrl+C и Ctrl+Break. Закрытие окна нажатием на "крестик" приложение в состоянии только обработать, но не предотвратить. Что, впрочем, тоже очень неплохо.

В приложении пример программы с исходным текстом, которая на время работы перехватывает и блокирует комбинации Ctrl+C и Ctrl+Break, а также обрабатывает принудительное закрытие консоли.

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

Console.CtrlHandler.Demo.zip (2,507 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (08.04.2018 в 14:13):
wet, в дополнение:

Цитата**Windows 7, Windows 8, Windows 8.1 and Windows 10: **

If a console application loads the gdi32.dll or user32.dll library, the HandlerRoutine function that you specify when you call SetConsoleCtrlHandler does not get called for the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT events. The operating system recognizes processes that load gdi32.dll or user32.dll as Windows applications rather than console applications. This behavior also occurs for console applications that do not call functions in gdi32.dll or user32.dll directly, but do call functions such as Shell functions that do in turn call functions in gdi32.dll or user32.dll.

Что в вольном переводе означает: если консольная программа, запущенная на Win7 и выше, импортирует напрямую, или каким-либо иным образом в процессе работы задействует библиотеки gdi32.dll или user32.dll, то события CTRL_LOGOFF_EVENT и CTRL_SHUTDOWN_EVENT в обработчик передаваться не будут. Для этого надо создавать невидимое окно и ставить перехват завершения работы на него.

А еще гуру М.Руссинович и Д.Соломон в своей книге "Внутреннее устройство Windows XP" пишут следующее:

ЦитатаОбнаружив консольное приложение, Csrss вызывает обработчик консоли, посылая событие CTRL_LOGOFF_EVENT (при завершении работы системы только процессы сервисов получают событие CTRL_SHUTDOWN_EVENT). Если обработчик возвращает FALSE,  Csrss уничтожает процесс. Если обработчик возвращает TRUE или не отвечает в течении определенного времени, Csrss выводит диалоговое окно принудительного завершения программы.

Сказанное, опять же, относится только к Windows XP. Так что пляски с бубном по этим двум событиям вообще запредельные, и просто взять и обработать их в обычном приложении не получится.
ManHunter (06.04.2018 в 07:32):
Есть такое дело. Но это я хочу добавить в статью про обработку перезагрузки и выключения компа, там оно будет смотреться более правильно.
wet (06.04.2018 в 05:24):
Ещё можно обработать CTRL_LOGOFF_EVENT смена пользователя и CTRL_SHUTDOWN_EVENT и выключение компа.

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

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

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