Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
385
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

Заметьте: код блока except в FuncMonkey никогда не вызовет MessageBeep. Опера тор return в блоке finally функции FuncPheasant заставит систему вообще прекратить раскрутку, и поэтому выполнение продолжичся так, будто ничего не произошло.

Microsoft намеренно вложила в SEH такую логику Иногда всдь нужно прекратить раскрутку и продолжить выполнение программы. Хотя в большинстве случаев так все же не делают А значит, будьте внимательны и избслайте операторов return в блоках finally.

EXCEPTION_CONTINUE_EXECUTION

Давайте приглядимся к юму, как фильтр исключений получает один из трсх иденти фикаторов, определенных в файле Excpt.h В Funcmeister2 идентификатор EXCEP TION_EXECUTE_HANDLER «зашит» (простоты ради) в код самого фильтра, но Вы могли бы вызывать там функцию, которая определяла бы нужный идентификатор Взгляните:

char g_szBuffer[100];

void FunclinRoosevfilt1()

{

int x = 0;

Char *pchBuffer = NULL;

__try

{

*pchBuffer = 'J';

x = 5 / x;

}

__except (OilFilter1(&pchBuffer))

{

MessageBox(NULL, "An exception occurred",

NULL, MB_OK);

}

MessageBox(NULL, Function completed , NULL, MB_OK),

}

LONG OilFilter1(char **ppchBuffer}

{

if (*ppchBuffer == NULL)

{

*ppchBuffer = g_szBuffer;

return(FXCEPTION_CONTINUE EXECUTION);

}

return(EXCEPTION_EXECUTE_HANDLER);

}

В первый раз проблема возникает, когда мы пытаемся поместить J в буфер, на который указывает pchBuffer К сожалению, мы не определили pchBuffer как указатель на наш глобальный буфер g_szBuffer — вместо этою он указывает на NULL. Процес сор генерирует исключение и вычисляет выражение в фильтре исключений в блоке except, связанном с блоком try, в котором и произошло исключение. В блоке cxcept адрес переменной pchBuffer передается функции OilFilter1,

Получая управление, OilFilter1 проверяет, не равен ли *ppchBuffer значению NULL, и, если да, устанавливает его так, чтобы он указывал на глобальный буфер g_szBuffer. Тогда фильтр возвращает EXCEPTION_CONTINUE_EXECUTION. Обнаружив гакое зна чение выражения в фильтре, система возвращается к инструкции, вызвавшей исклю чение, и пытается выполнить ее снова. IIa этот раз все проходит успешно, и J будет записана в первый байт буфера g_szBuffer.

Когда выполнение кода продолжится, мы опять столкнемся с проблемой в блоке try — теперь это деление на нуль. И вновь система вычислит выражение фильтра ис

ключений. На этот раз *ppchBuffer не равен NULL, и поэтому OilFttterl вернет EXCEP TION_EXECUTE_HANDLER, что подскажет системе выполнить код в блоке excepf, и на экране появится окно с сообщением oб исключении.

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

Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION

Будет ли удачной попытка исправить ситуацию в только что рассмотренной функ ции и заставить систему продолжить выполнение программы, зависит от типа про цессора, от того, как компилятор генерирует машинные команды при трансляции операторов С/С++, и от параметров, заданных компилятору

Компилятор мог сгенерировать две машинные команды для оператора *pchBuffer = 'J'; которые выглядят так:

MOV EAX, [pchBuffer] // адрес помещается в регистр EAX

MOV [EAX], 'J' // символ J записывается по адресу из регистра LAX

Последняя команда и возбудила бы исключение. Фильтр исключений, перехватив его, исправил бы значение pchBuffer и указал бы системе повторить эту команду. Но проблема в том, что содержимое регистра не изменится так, чтобы отразить новое значение pchBuffer, и поэтому повторение команды снова приведет к исключению. Вот и бесконечный цикл!

Выполнение программы благополучно возобновится, если компилятор оптими зирует код, но может прерваться, если компилятор код не оптимизирует. Обнаружить такой «жучок» очень трудно, и — чтобы определить, откуда он взялся в программе, — придется