
Рихтер Дж., Назар К. - Windows via C C++. Программирование на языке Visual C++ - 2009
.pdf
764 Часть V. Структурная обработка исключений
Перед завершением главного потока выводится последнее сообщение об отсутствии необработанных исключений:
После того как вы закроете и это окно, процесс нормально завершится, поскольку функция _tWinMain вернет управление. Заметьте, что данное окно не появляется при аварийном завершении процесса.
А сейчас снова запустим эту программу. Но на этот раз попробуйте щелкнуть кнопку Yes, чтобы обратиться к памяти по недопустимому адресу (попытаться записать 5 по адресу NULL). В результате возникает необработанное нарушение доступа и в Windows XP открывается следующее окно.
Рис. 23-1. Сообщение о необработанном исключении в Windows XP
В Windows Vista соответствующее окно выглядит так:
Рис. 23-2. Первое сообщение о необработанном исключении в Windows Vista

Глава 23. Обработчики завершения.docx 765
Если закрыть его щелчком кнопки Cancel, приложение по-тихому завершится. В противном случае выводится окно следующего пила:
Рис. 23-3. Второе сообщение о необработанном исключении в Windows Vista
Щелчок кнопки Debug система выполняет действия, описанные в главе 25, и запускает отладчик, который подключается к сбойному процессу. Если вместо этого щелкнуть Close Program (в Windows Vista), Send Error Report либо Don't Send (в Windows XP), процесс завершится. Однако в коде программы-примера есть блок finally, поэтому он должен быть исполнен перед завершением процесса. При этом в Windows XP вывод1ггся следующее сообщение:
Блок finally исполняется, поскольку код в блоке try завершился аварийно. Если закрыть это окно, процесс действительно завершится. Однако это происходит только в версиях Windows, предшествующих Vista, в которых блок finally исполняется только в случае глобальной раскрутки. Как сказано в главе 6, точка входа потока защищена блоком try/except. В этом случае вызывается функция фильтра исключений в __except(), которая должна вернуть EXCEPTION_EXECUTE_ HANDLER. В Windows Vista механизмы необработанных исключений были в значительной степени переделаны с целью повышения надежности (см. главу 25). Сразу бросается в глаза один из недостатков этих нововведений: возвращается фильтр исключений, являющийся также его оболочкой, возвращает EXCEPTION_CONTINUE_SEARCH, поэтому процесс тут же завершается без шансов на исполнение блока finally.
Код SEHTerm.exe проверяет, в какой системе он работает. Если это Windows Vista, следующее окно позволяет указать, следует ли защитить сбойную функцию блоком try/except.

766 Часть V. Структурная обработка исключений
Если щелкнуть кнопку Yes, блок try/finally будет защищен фильтром исключений, который всегда возвращает EXCEPTION_EXECUTE_HANDLER. При этом в случае возникновения исключения непременно начинается глобальная раскрутка, и при исполнении блока finally открывается окно следующего вида:
Перед завершением главного потока приложения с кодом -1 блок except выводит следующее сообщение:
Если же щелкнуть кнопку No, при возникновении исключения приложение завершится без исполнения блока finally (если вы не выберете запуск отладки).
SEHTerm.cpp
/****************************************************************************** Module: SEHTerm.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */ #include <windows.h>
#include <tchar.h>
///////////////////////////////////////////////////////////////////////////////
BOOL IsWindowsVista() {
//Code from Chapter 4
//Prepare the OSVERSIONINFOEX structure to indicate Windows Vista. OSVERSIONINFOEX osver = { 0 };
osver.dwOSVersionInfoSize = sizeof(osver); osver.dwMajorVersion = 6; osver.dwMinorVersion = 0; osver.dwPlatformId = VER_PLATFORM_WIN32_NT;

Глава 23. Обработчики завершения.docx 767
// Prepare the condition mask.
DWORDLONG dwlConditionMask = 0; // You MUST initialize this to 0. VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
// Perform the version test.
if (VerifyVersionInfo(&osver, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwlConditionMask)) {
//The host system is Windows Vista exactly. return(TRUE);
}else {
//The host system is NOT Windows Vista. return(FALSE);
}
}
void TriggerException() {
__try {
int n = MessageBox(NULL, TEXT("Perform invalid memory access?"), TEXT("SEHTerm: In try block"), MB_YESNO);
if (n == IDYES) {
* (PBYTE) NULL = 5; // This causes an access violation
}
}
__finally {
PCTSTR psz = AbnormalTermination()
? TEXT("Abnormal termination") : TEXT("Normal termination"); MessageBox(NULL, psz, TEXT("SEHTerm: In finally block"), MB_OK);
}
MessageBox(NULL, TEXT("Normal function termination"), TEXT("SEHTerm: After finally block"), MB_OK);
}
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, PTSTR, int) {
//In Windows Vista, a global unwind occurs if an except filter
//returns EXCEPTION_EXECUTE_HANDLER. If an unhandled exception
//occurs, the process is simply terminated and the finally blocks
//are not exectuted.
if (IsWindowsVista()) {

768 Часть V. Структурная обработка исключений
DWORD n = MessageBox(NULL, TEXT("Protect with try/except?"),
TEXT("SEHTerm: workflow"), MB_YESNO);
if (n == IDYES) { __try {
TriggerException();
}
__except (EXCEPTION_EXECUTE_HANDLER) {
//But the system dialog will not appear.
//So, popup a message box.
MessageBox(NULL, TEXT("Abnormal process termination"),
TEXT("Process entry point try/except handler"), MB_OK);
// Exit with a dedicated error code return(-1);
}
} else { TriggerException();
}
} else { TriggerException();
}
MessageBox(NULL, TEXT("Normal process termination"),
TEXT("SEHTerm: before leaving the main thread"), MB_OK);
return(0);
}
//////////////////////////////// End of File //////////////////////////////////
Оглавление |
|
Г Л А В А 2 4 Фильтры и обработчики исключений .......................................................... |
769 |
Примеры использования фильтров и обработчиков исключений................................ |
770 |
Funcmeister1........................................................................................................................ |
770 |
Funcmeister2........................................................................................................................ |
771 |
EXCEPTION_EXECUTE_HANDLER......................................................................................... |
773 |
Некоторые полезные примеры..................................................................................... |
774 |
Глобальная раскрутка...................................................................................................... |
777 |
Остановка глобальной раскрутки ................................................................................ |
780 |
EXCEPTION_CONTINUE_EXECUTION.................................................................................... |
782 |
Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION................................... |
783 |
EXCEPTION_CONTINUE_SEARCH.......................................................................................... |
784 |
Функция GetExceptionCode............................................................................................. |
786 |
Функция GetExceptionInformation ................................................................................. |
791 |
Программные исключения ............................................................................................. |
795 |
Г Л А В А 2 4
Фильтры и обработчики исключений
Исключение — это событие, которого вы не ожидали. В хорошо написанной программе не предполагается попыток обращения по неверному адресу или деления на нуль. И все же такие ошибки случаются. За перехват попыток обращения по неверному адресу и деления на нуль отвечает центральный процессор, возбуждающий исключения в ответ на эти ошибки. Исключение, возбужденное процессором, называется аппаратным (hardware exception). Далее мы увидим, что операционная система и прикладные программы способны возбуждать собственные исключения — программные (software exceptions).
При возникновении аппаратного или программного исключения операционная система дает вашему приложению шанс определить его тип и самостоятельно обработать. Синтаксис обработчика исключений таков:
_try {
// защищенный блок
…
}
__except (exception filter) { // обработчик исключений
…
}
Обратите внимание на ключевое слово __cxcept. За блоком try всегда должен следовать либо блок finnаllу, либо блок except. Для данного блока try нельзя указать одновременно и блок finally, и блок except; к тому же за try не может следовать несколько блоков finally или except. Однако try-finally можно вложить в tryexcept, и наоборот.
770 Часть V. Структурная обработка исключений
Примеры использования фильтров и обработчиков исключений
В отличие от обработчиков завершения (рассмотренных в предыдущей главе), фильтры и обработчики исключений выполняются непосредственно операционной системой — нагрузка на компилятор при этом минимальна. В следующих разделах я расскажу, как обычно выполняются блоки try-except, как и когда операционная система проверяет фильтры исключений и в каких случаях она выполняет код обработчиков исключений.
Funcmeister1
Вот более конкретный пример блока try-except:
DWORD Funcmeister1() {
DWORD dwTemp;
// 1. Что-то делаем здесь
…
_try {
// 2. Выполняем какую-то операцию dwTemp = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// обрабатываем исключение; этот код никогда не выполняется
…
}
// 3. Продолжаем что-то делать return(dwTemp);
}
В блоке try функции Funcmeister1 мы просто присваиваем 0 переменной dwTemp. Такая операция не приведет к исключению, и поэтому код в блоке except никогда не выполняется. Обратите внимание на такую особенность: конструкция try-finally ведет себя иначе. После того как переменной dwTemp присваивается 0, следующим исполняемым оператором оказывается return.
Хотя ставить операторы return, goto, continue и break в блоке try обработчика завершения настоятельно не рекомендуется, их применение в этом блоке не приводит к снижению быстродействия кода или к увеличению его размера. Использование этих операторов в блоке try, связанном с блоком except, не вызовет таких неприятностей, как локальная раскрутка.
Глава 24. Фильтры и обработчики исключений.docx 771
Funcmeister2
Попробуем модифицировать нашу функцию и посмотрим, что будет:
DWORD Funcmeister2() {
DWORD dwTemp = 0; |
|
// 1. Что-то делаем здесь |
|
… |
|
_try { |
|
// 2. Выполняем какую-то операцию |
|
dwTemp = 5 / dwTemp; |
// генерирует исключение |
dwTemp += 10; |
// никогда не выполняется |
}
__except ( /* 3. Проверяем фильтр. */ EXCEPTION_EXECUTE_HANDLER) { // 4. Обрабатываем исключение.
MessageBeep(0);
…
}
// 5. Продолжаем что-то делать eturn(dwTemp);
}
Инструкция внутри блока try функции Funcmeister2 пытается поделить 5 на 0. Перехватив это событие, процессор возбуждает аппаратное исключение. Тогда операционная система ищет начало блока except и проверяет выражение, указанное в качестве фильтра исключений; оно должно дать один из трех идентификаторов, определенных в заголовочном Windows-файле Excpt.h.
Табл. 24-1. Значения, возвращаемые фильтром исключений
Идентификатор |
Значение |
EXCEPTION_EXECUTE_HANDLER |
1 |
EXCEPTION_CONTINUE_SEARCH |
0 |
EXCEPTION_CONTINUE_EXECUTION |
-1 |
Далее мы обсудим, как эти идентификаторы изменяют выполнение потока. Читая следующие разделы, посматривайте на блок-схему на рис. 24-1, которая иллюстрирует операции, выполняемые системой после генерации; исключения.