Под ошибками понимаются исключительные ситуации, которые время от времени могут возникать в известных местах программы. Так, обнаружение ошибок, возникающих во время выполнения системных вызовов, и немедленный вывод сообщений о них должны предусматриваться логикой работы самой программы. Поэтому программисты, как правило, явно включают в программный код участки, ответственные, например, за тестирование успешности завершения операции чтения данных из файла. В главе 2 для диагностики ошибок и принятия соответствующих мер была разработана функция ReportError.
С другой стороны, исключения могут возникать практически в любом месте программы, и поэтому организация явной проверки всех исключений невозможна или практически нецелесообразна. Примерами подобных ситуаций могут служить попытки деления на ноль или обращения к недоступным областям памяти.
Вместе с тем, указанные различия между ошибками и исключениями являются довольно условными. Windows позволяет управлять генерацией исключений, возникающих в случае нехватки памяти при ее распределении с использованием функций НеарАllос и HeapCreate. Этот процесс описан в главе 5. Помимо этого, программы могут генерировать собственные исключения с кодами, определяемыми программистом, используя для этого функцию RaiseException, о чем далее будет говориться.
Обработчики исключений обеспечивают удобный механизм выхода из внутренних блоков или функций под управлением программы без использования операторов перехода goto или longjmp. Такая возможность оказывается особенно полезной, если блок получил доступ к таким, например, ресурсам, как открытые файлы, память или объекты синхронизации, поскольку обработчик может взять на себя задачу освобождения этих ресурсов. Возможно также продолжение работы программы после выполнения кода обработчика исключений, а не ее обязательное завершение. Кроме того, после выхода из блока программа может восстанавливать прежнее состояние системы, например маску FP-исключений. Именно в этом ключе обработчики используются во многих наших примерах.
Исключения, генерируемые приложением
Существует возможность формирования исключений в любой точке программы в процессе ее выполнения с помощью функции RaiseException. Это позволяет программе обнаруживать и обрабатывать возникающие ошибки как исключения.
VOID RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD cArguments, CONST DWORD *lpArguments)
Параметры
dwExceptionCode — код исключения, определяемый пользователем. Бит 28 использовать нельзя, так как он зарезервирован системой. Для кода ошибки отводятся биты 27—0 (то есть все слово, кроме самого старшего шестнадцатеричного разряда). Бит 29 должен быть установлен, чтобы показать, что данное исключение имеет "пользовательскую" природу (а не относится к числу тех, которые предусмотрела Microsoft). В битах 31—30 содержится код серьезности ошибки, принимающий приведенные ниже значения, в которых результирующая старшая шестнадцатеричная цифра кода исключения представлена с установленным битом 29.
• 0 — успешное выполнение (старшая шестнадцатеричная цифра кода исключения равна 2).
• 1 — информационный код (старшая шестнадцатеричная цифра кода исключения равна 6).
• 2 — предупреждение (старшая шестнадцатеричная цифра кода исключения равна А).
• 3 — ошибка (старшая шестнадцатеричная цифра кода исключения равна Е).
dwExceptionFlags — обычно устанавливается равным 0, тогда как установка значения EXCEPTION_NONCONTINUABLE будет указывать на то, что выражение фильтра не должно возвращать значение EXCEPTION_CONTINUE_EXECUTION; при попытке это сделать будет немедленно сгенерировано исключение ЕХСЕРTION_NONCONTINUABLE_EXCEPTION.
lpArguments — этот указатель, если он не равен NULL, указывает на массив размера cArguments (третий параметр), содержащий 32-битовые значения, которые должны быть переданы выражению фильтра. Максимально возможное число этих значений ограничивается значением EXCEPTION_MAXIMUM_PARAMETERS, которое в настоящее время установлено равным 15. Для доступа к этой структуре следует использовать функцию GetExceptionInformation.
Заметьте, что невозможно сгенерировать исключение в другом процессе. В то же время, при весьма ограниченных условиях для этой цели могут быть использованы обработчики управляющих сигналов консоли, о чем говорится в конце этой главы и в главе 6.
Пример: обработка ошибок как исключений
В предыдущих примерах для обработки ошибок при выполнении системных вызовов и других ошибок используется функция ReportError. Эта функция прекращает выполнение процесса, если программист указал, что данная ошибка является критической. Вместе с тем, такой подход препятствует нормальному выходу из программы и не обеспечивает возможность продолжения работы программы после устранения последствий ошибки. Так, после отказа от задачи, которая привела к возникновению сбоя, может потребоваться уничтожение временных файлов, созданных в процессе работы программы, или переход программы к выполнению других задач. Функции ReportError присущи и другие ограничения, перечень которых приводится ниже.
Читать дальше