Системное программирование в среде Windows
Шрифт:
Во втором элементе структуры EXCEPTION_POINTERS, а именно, элементе ContextRecord, содержится информация, относящаяся к процессору. Для каждого типа процессоров предусмотрены свои структуры, определения которых содержатся в файле <winnt.h>.
Резюме: последовательность обработки исключений
На рис. 4.2 в схематическом виде представлена последовательность событий, происходящих после возникновении исключения. Слева приведен программный код, а обведенные кружками цифры справа обозначают операции, выполняемые языковыми средствами поддержки времени выполнения. Отдельные элементы приведенной схемы имеют следующий смысл:
1. Возникло
2. Управление передается обработчику исключений, в котором вычисляется выражение фильтра. Сначала вызывается функция GetExceptionCode, а затем ее возвращаемое значение используется в качестве аргумента функции Filter.
3. Функция фильтра выполняет действия, определяемые значением кода исключения.
4. В данном случае значением кода исключения является EXCEPTION_INT_DIVIDE_BY_ZERO.
5. Функция фильтра устанавливает, что должен быть выполнен код обработчика исключений, и поэтому возвращает значение EXCEPTION_EXECUTE_HANDLER.
6. Выполняется код обработчика исключений, связанного с оператором _except.
7. Управление передается за пределы блоков try и except.
Рис. 4.2. Последовательность операций при обработке исключений
Исключения, возникающие при выполнении операций над числами с плавающей точкой
Существует семь различных кодов исключений, которые могут возникать при выполнении операций с использованием данных вещественного типа. Первоначально эти исключения отключены и не могут возникать до тех пор, пока с помощью функции _controlfp для них не будет предварительно задана специальная маска, не зависящая от типа процессора. Предусмотрены отдельные исключения для ситуаций антипереполнения, переполнения, деления на ноль, неточного результата и так далее, что иллюстрируется приведенным ниже фрагментом кода. Для активизации исключений определенного типа следует отключить соответствующий бит маски.
Фактическое значение маски определяется ее текущим значением (current_mask) и двумя аргументами следующим образом:
Данная функция устанавливает лишь те из битов, указанных в аргументе new, которые разрешены аргументом mask. Биты, не активизированные аргументом mask, не изменяются. Маска FP-исключений управляет также точностью, округлением и обработкой значений, соответствующих бесконечности, поэтому при активизации перечисленных исключений необходимо тщательно следить за тем, чтобы случайно не изменить эти установки.
Возвращаемым значением является фактическое значение маски. Так, при нулевых значениях обоих аргументов возвращаемым значением будет текущее значение маски (current_mask), что может быть использовано для восстановления маски, если впоследствии в этом возникнет необходимость. С другой стороны, если задать аргумент mask равным 0xFFFFFFFF, то регистр установится в new, что, например, может быть использовано для восстановления прежнего значения маски.
Обычно для того, чтобы разрешить исключения, связанные с выполнением операций над числами с плавающей точкой, в качестве аргумента mask используют константу MCW_EM, как продемонстрировано в следующем примере. Также заметьте, что при обработке FP-исключения оно должно быть сброшено путем использования функции _clearfp.
В этом примере разрешены все возможные FP-исключения, кроме одного — EXCEPTION_FLT_STACK_CHECK, которое соответствует переполнению стека при выполнении операций над числами с плавающей точкой. Можно поступить и по-другому, разрешая отдельные исключения путем использования только выбранных масок исключений, например EM_OVERFLOW. Аналогичный код используется в программе 4.3 в контексте примера программного кода большего объема.
Ошибки и исключения
Под ошибками понимаются исключительные ситуации, которые время от времени могут возникать в известных местах программы. Так, обнаружение ошибок, возникающих во время выполнения системных вызовов, и немедленный вывод сообщений о них должны предусматриваться логикой работы самой программы. Поэтому программисты, как правило, явно включают в программный код участки, ответственные, например, за тестирование успешности завершения операции чтения данных из файла. В главе 2 для диагностики ошибок и принятия соответствующих мер была разработана функция ReportError.
С другой стороны, исключения могут возникать практически в любом месте программы, и поэтому организация явной проверки всех исключений невозможна или практически нецелесообразна. Примерами подобных ситуаций могут служить попытки деления на ноль или обращения к недоступным областям памяти.
Вместе с тем, указанные различия между ошибками и исключениями являются довольно условными. Windows позволяет управлять генерацией исключений, возникающих в случае нехватки памяти при ее распределении с использованием функций НеарАllос и HeapCreate. Этот процесс описан в главе 5. Помимо этого, программы могут генерировать собственные исключения с кодами, определяемыми программистом, используя для этого функцию RaiseException, о чем далее будет говориться.
Обработчики исключений обеспечивают удобный механизм выхода из внутренних блоков или функций под управлением программы без использования операторов перехода goto или longjmp. Такая возможность оказывается особенно полезной, если блок получил доступ к таким, например, ресурсам, как открытые файлы, память или объекты синхронизации, поскольку обработчик может взять на себя задачу освобождения этих ресурсов. Возможно также продолжение работы программы после выполнения кода обработчика исключений, а не ее обязательное завершение. Кроме того, после выхода из блока программа может восстанавливать прежнее состояние системы, например маску FP-исключений. Именно в этом ключе обработчики используются во многих наших примерах.