Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION
Будет ли удачной попытка исправить ситуацию в только что рассмотренной функ ции и заставить систему продолжить выполнение программы, зависит от типа про цессора, от того, как компилятор генерирует машинные команды при трансляции операторов С/С++, и от параметров, заданных компилятору
Компилятор мог сгенерировать две машинные команды для оператора *pchBuffer = 'J'; которые выглядят так:
MOV EAX, [pchBuffer] // адрес помещается в регистр EAX
MOV [EAX], 'J' // символ J записывается по адресу из регистра LAX
Последняя команда и возбудила бы исключение. Фильтр исключений, перехватив его, исправил бы значение pchBuffer и указал бы системе повторить эту команду. Но проблема в том, что содержимое регистра не изменится так, чтобы отразить новое значение pchBuffer, и поэтому повторение команды снова приведет к исключению. Вот и бесконечный цикл!
Выполнение программы благополучно возобновится, если компилятор оптими зирует код, но может прерваться, если компилятор код не оптимизирует. Обнаружить такой "жучок" очень трудно, и — чтобы определить, откуда он взялся в программе, — придется анализировать ассемблерный текст, сгенерированный для исходного кода. Вывод: будьте крайне осторожны, возвращая EXCEPTION_CONTINUE_EXECUTION из фильтра исключений.
EXCEPTION_CONTINUE_EXECUTION всегда срабатывает лишь в одной ситуации: при передаче памяти зарезервированному региону. О том, как зарезервировать боль шую область адресного пространства, а потом передавать ей память лишь по мере необходимости, я рассказывал в главе 15 Соответствующий алгоритм демонстриро вала программа-пример VMAlloc. На основе механизма SEH то же самое можно было бы реализовать гораздо эффективнее (и не пришлось бы все время вызывать функ цию VirtualAtloc).
В главе l6 мы говорили о стеках потоков, В частности, я показал, как система ре зервирует для стека потока регион адресного пространства размером 1 Мб и как она автоматически передает ему новую память по мере разрастании стека. С этой целью система создает SEH-фрейм. Когда поток пытается задействовать несуществующую часть стека, генерируется исключение. Системный фильтр определяет, что исключе ние возникло из-за попытки обращения к адресному пространству, зарезервирован ному под стек, вызывает функцию VirtualAlloc для передачи дополнительной памяти стеку потока и возвращает EXCEPTION_CONTINUE_EXECUTION. После этого машин ная команда, пытавшаяся обратиться к несуществующей части стека, благополучно выполняется, и поток продолжает свою работу.
Механизмы использования виртуальной памяти в сочетании со структурной об работкой исключений позволяют создавать невероятно "шустрые* приложения Про грамма-пример Spreadsheet в следующей главе продемонстрирует, как на основе SEH
эффективно реализовать управление памятью в электронной таблице. Этот код вы полняется чрезвычайно быстро.