Обработчики управляющих сигналов консоли
Обработчики исключений могут реагировать на самые разнообразные события, но они не в состоянии обнаруживать такие ситуации, как выход пользователя из системы или нажатие комбинации клавиш <Ctrl+C> на клавиатуре с целью прекращения выполнения программы. Для обработки таких событий требуются обработчики управляющих сигналов консоли.
Функция SetConsoleCtrlHandler позволяет одной или нескольким указанным функциям выполняться в ответ на получение сигналов Ctrl-c, Ctrl-break или одного из трех других сигналов, связанных с консолью. Функция GenerateConsoleCtrlEvent, описанная в главе 6, также генерирует эти сигналы, а, кроме того, все эти сигналы могут посылаться другим процессам, совместно использующим ту же консоль. Обработчиками сигналов являются указанные пользователем функции, которые возвращают булевские значения и принимают единственный аргумент типа DWORD, идентифицирующий фактический сигнал.
С одним сигналом могут быть ассоциированы несколько обработчиков, причем обработчики можно добавлять и удалять. Функция, которая используется для добавления и удаления обработчиков, имеет следующий вид:
BOOL SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add)
Значению флага Add, равному TRUE, соответствует добавление процедуры обработчика, в противном случае происходит удаление процедуры из списка процедур обработки управляющих сигналов консоли. Заметьте, что тип сигнала при вызове функции не конкретизируется. Тестирование с целью проверки того, какой именно сигнал получен, должен выполнять сам обработчик.
Процедура обработчика возвращает булевское значение и принимает единственный параметр типа DWORD, идентифицирующий фактический сигнал. Использованное в объявлении имя обработчика (HandlerRoutine) является заменителем, и программист может выбирать его по своему усмотрению.
Ниже приводятся дополнительные полезные сведения, касающиеся использования обработчиков управляющих сигналов консоли.
• Если значение параметра HandlerRoutine равно NULL, а параметра Add — TRUE, то сигналы Ctrl-c будут игнорироваться.
• Если при вызове функции SetConsoleMode был задан параметр ENABLE_PROCESSED_INPUT (глава 2), то комбинация <Ctrl+C> будет обрабатываться не как сигнал, а как клавиатурный ввод.
• Процедура обработчика фактически выполняется как независимый поток (см. главу 7) внутри процесса. При этом выполнение основной программы, как показано в следующем примере, не приостанавливается.
• Формирование исключения в обработчике не вызовет исключения в потоки, выполнение которого было прервано, поскольку исключения применяются только к потокам, а не к процессу в целом. Если вы хотите организовать связь с прерванным потоком, используйте переменную, как в следующем примере, или метод синхронизации (глава 8).
Между исключениями и сигналами существует важное отличие. Сигналы применяются к процессу в целом, тогда как исключения — только к потоку, выполняющему код, в котором возникло исключение.
BOOL HandlerRoutine(DWORD dwCtrlType)
dwCtrlType идентифицирует фактический сигнал (или событие) и может принимать одно из следующих пяти значений:
1. CTRL_C_EVENT указывает на то, что комбинация <Ctrl+C> должна восприниматься как клавиатурный ввод.
2. CTRL_CLOSE_EVENT указывает на закрытие окна консоли.
3. CTRL_BREAK_EVENT указывает на сигнал Ctrl-break.
4. CTRL_LOGOFF_EVENT указывает на выход пользователя из системы.
5. CTRL_SHUTDOWN_EVENT указывает на завершение работы системы.
Обработчик сигналов может выполнять операции по "уборке мусора" точно так же, как это делают обработчики исключений и завершения. В случае успешной обработки сигнала обработчик должен вернуть значение TRUE. Если обработчик возвращает значение FALSE, выполняется следующая функция обработчика из числа тех, что указаны в списке. Обработчики сигналов выполняются в порядке, обратном порядку их установки, так что первым будет выполняться самый последний из установленных обработчиков, а системный обработчик будет выполняться самым последним.
Пример: обработчик управляющих сигналов консоли
В программе 4.5 организован бесконечный цикл, в котором каждые 5 секунд вызывается функция Веер, подающая звуковой сигнал. Пользователь может завершить выполнение программы, нажав комбинацию клавиш <Ctrl+C> или закрыв консоль. Процедура обработчика выводит на экран сообщение, выжидает 10 секунд, после чего, казалось бы, выполнение программы должно завершиться с возвратом значения TRUE. Однако в действительности основная программа обнаруживает флаг Exit и останавливает процесс. Это демонстрирует параллельную природу выполнения процедуры обработчика; заметьте, что объем выходной информации обработчика сигналов зависит от временных характеристик сигнала. Обработчики управляющих сигналов консоли будут использоваться также в примерах, приводимых в следующих главах.
Обратите внимание на использование макроса WINAPI; он применяется к пользовательским функциям, передаваемым в качестве аргументов функциям Windows, чтобы гарантировать выполнение соответствующих соглашений о вызовах. Этот макрос определен в заголовочном файле Microsoft С WTYPES.Н.
Программа 4.5. Ctrlc: программа обработки сигналов
/* Глава 4. Ctrlc.с */
/* Перехватчик событий консоли. */
#include "EvryThng.h"
static BOOL WINAPI Handler(DWORD CtrlEvent); /* См. WTYPES.H. */
volatile static BOOL Exit = FALSE;
int _tmain(int argc, LPTSTR argv[])
/* Периодическая подача звукового сигнала до поступления сигнала о прекращении выполнения. */
{
/* Добавить обработчик событий. */
if (!SetConsoleCtrlHandler(Handler, TRUE)) ReportError(_T("Ошибка при установке обработчика событий."), 1, TRUE);
while (!Exit) {
Sleep (5000); /* Подача звукового сигнала каждые 5 секунд. */
Веер(1000 /* Частота. */, 250 /* Длительность. */);
}
_tprintf(_T("Прекращение выполнения программы по требованию.\n"));
return 0;
}
BOOL WINAPI Handler (DWORD CtrlEvent) {
Exit = TRUE;
switch (CntrlEvent) {
/* Увидите ли вы второе сообщения обработчика, зависит от соотношения временных параметров. */
case CTRL_C_EVENT:
_tprintf(_T("Получен сигнал Ctrl-c. Выход из обработчика через 10 секунд.\n"));
Sleep(4000); /* Уменьшите это значение, чтобы получить другой эффект. */
_tprintf(_T("Выход из обработчика через 6 секунд.\n"));
Sleep(6000); /* Попробуйте уменьшить и это значение. */
return TRUE; /* TRUE указывает на успешную обработку сигнала. */
case CTRL_CLOSE_EVENT:
_tprintf(_T("Выход из обработчика через 10 секунд.\n"));
Sleep(4000);
_tprintf(_Т ("Выход из обработчика через 6 секунд.\n"));
Sleep (6000); /* Попробуйте уменьшить и это значение. */
return TRUE; /* Попробуйте возвратить FALSE. Приводит ли это к изменению поведения программы? */
default:
_tprintf(_T("Событие: %d. Выход из обработчика через 10 секунд.\n"), CntrlEvent);
Sleep(4000);
_tprintf(_T("Выход из обработчика через 6 секунд.\n"));
Sleep(6000);
return TRUE;
}
}