Visual studio обработка ошибок

Обновлено: 03.07.2024

Ни одна серьезная программа не может обойтись без собственных обработчиков исключений, так как во время выполнения программы рано или поздно могут случиться разные «неожиданности». Например, пользователь введет неверные данные и компьютеру придется выполнять деление на ноль. Или исчезнет куда-нибудь файл, необходимый для работы программы. Конечно, ничего страшного не произойдет, так как операционная система обрабатывает подобные неприятности. Но программа при этом завершится аварийно, а это уже грозит не только потерей несохраненных данных, но и серией нецензурных выражений в адрес программиста со стороны пользователя. А это как раз тот редкий случай, когда пользователь абсолютно прав. Поэтому любой мало-мальски грамотный программист должен предусмотреть возможное появление ошибок в ходе выполнения программы и принять соответствующие меры.

Эта статья посвящена обработке исключений. В качестве примеров используются языковые конструкции Visual C++ 6.5.

1. Фреймовая обработка исключений

1.1. Исключения и их обработчики

Исключение – это событие, которое произошло во время выполнения программы, в результате совершения которого дальнейшее нормальное выполнение программы становится невозможным. Обычно такие события происходят из-за ошибок в программе или неправильных действий пользователя (впрочем, хороший программист не может рассчитывать на то, что пользователь всегда будет действовать правильно). После возникновения исключения требуется привести программу в рабочее состояние или выполнить её аварийное завершение с освобождением всех ресурсов, которые использовались программой.

Для выполнения описанных выше действий в операционных системах Windows предназначен механизм структурной обработки исключений (structured exception handling, SHE). Работает это так. В программе выделяется блок программного кода, где может произойти исключение. Этот блок кода называется фреймом, а сам код называется охраняемым кодом. После фрейма вставляется программный блок, где обрабатывается исключение. Этот блок называется обработчиком исключения. Когда исключение будет обработано, управление передается первой инструкции, которая следует за обработчиком исключения.

  • EXCEPTION_EXECUTE_HANDLER – управление передается обработчику исключений;
  • EXCEPTION_CONTINUE_SEARCH – система продолжает поиск обработчика исключения;
  • EXCEPTION_CONTINUE_EXECUTION – система передает управление в точку прерывания программы.

Переменные, объявленные внутри фрейма или блока обработки исключения, являются локальными и видны только внутри соответствующего блока, как это принято в С++. Пример обработки исключения:

1.2. Как получить код исключения

  • EXCEPTION_ACCESS_VIOLATION – попытка чтения или записи в виртуальную память без соответствующих прав доступа;
  • EXCEPTION_BREAKPOINT – встретилась точка останова;
  • EXCEPTION_DATATYPE_MISALIGNMENT – доступ к данным, адрес которых не выровнен по границе слова или двойного слова;
  • EXCEPTION_SINGLE_STEP – механизм трассировки программы сообщает, что выполнена одна инструкция;
  • EXCEPTION_ARRAY_BIUNDS_EXCEEDED – выход за пределы массива, если аппаратное обеспечение поддерживает такую проверку;
  • EXCEPTION_FLT_DENORMAL_OPERAND – один из операндов с плавающей точкой является ненормализованным;
  • EXCEPTION_FLT_DIVIDE_BY_ZERO – попытка деления на ноль в операции с плавающей точкой;
  • EXCEPTION_FLT_INEXACT_RESULT – результат операции с плавающей точкой не может быть точно представлен десятичной дробью;
  • EXCEPTION_FLT_INVALID_OPERATION – ошибка в операции с плавающей точкой, для которой не предусмотрены другие коды исключения;
  • EXCEPTION_FLT_OVERFLOW – при выполнении операции с плавающей точкой произошло переполнение;
  • EXCEPTION_FLT_STACK_CHECK – переполнение или выход за нижнюю границу стека при выполнении операции с плавающей точкой;
  • EXCEPTION_FLT_UNDERFLOW – результат операции с плавающей точкой является числом, которое меньше минимально возможного числа с плавающей точкой;
  • EXCEPTION_INT_DIVIDE_BY_ZERO – попытка деления на ноль при операции с целыми числами;
  • EXCEPTION_INT_OVERFLOW – при выполнении операции с целыми числами произошло переполнение;
  • EXCEPTION_PRIV_INSTRUCTION – попытка выполнения привилегированной инструкции процессора, которая недопустима в текущем режиме процессора;
  • EXCEPTION_NONCONTINUABLE_EXCEPTION – попытка возобновления исполнения программы после исключения, которое запрещает выполнять такое действие.

1.3. Функции фильтра

Если есть необходимость более детально обработать информацию об исключении, то в выражении-фильтре используют функцию, которая в этом случае называется функцией фильтра. В функции фильтра нельзя вызывать функции GetExceptionCode и GetExceptionInformation. Однако эти функции могут вызываться для инициализации параметров функции фильтра.

Пример программы, в которой используется функция фильтра для принятия решения о дальнейшей обработке исключения, приведён ниже. Здесь функция фильтра (ff) возвращает одно из двух значений EXCEPTION_CONTINUE_EXECUTION или EXCEPTION_EXECUTE_HANDLER. Первое значение возвращается в том случае, если исключение генерируется системой при целочисленном делении на ноль, а второе – в остальных случаях. При попытке деления на ноль происходит исключение и в качестве выражения-фильтра применяется результат выполнения функции ff. Эта функция проверяет, чем было вызвано исключение, и если это деление на ноль, то ошибка исправляется (а = 10). Затем функция возвращает значение EXCEPTION_CONTINUE_EXECUTION, то есть программа продолжает свою работу, но уже с исправленным значением переменной a. Если же это исправление не сделать, то программа войдет в бесконечный цикл.

1.4. Необработанные исключения

  • EXCEPTION_CONTINUE_SEARCH – передать управление отладчику приложения;
  • EXCEPTION_EXECUTE_HANDLER – передать управление обработчику исключений.
  • EXCEPTION_EXECUTE_HANDLER – выполнение программы прекращается;
  • EXCEPTION_CONTINUE_EXECUTION – возобновить исполнение программы с точки исключения;
  • EXCEPTION_CONTINUE_SEARCH – выполняется системная функция UnhandledExceptionFilter.

1.5. Обработка исключений при операциях с плавающей точкой

По умолчанию система отключает все исключения с плавающей точкой. Поэтому если при выполнении операции с плавающей точкой было получено число, которое не входит в диапазон представления чисел с плавающей точкой, то в результате система вернет NAN или INFINITY в случае слишком малого или слишком большого числа соответственно. Чтобы включить режим генерации исключений с плавающей точкой нужно изменить состояние слова, управляющего обработкой операций с плавающей точкой. Это можно сделать при помощи функции _controlfp, которая имеет следующий прототип: Прототип определен в заголовочном файле float.h. Эта функция возвращает старое слово, управляющее обработкой исключений. Параметр new задает новое управляющее слово, а параметр mask должен принимать значение _MCW_EM. Если значение этого параметра равно 0, то функция возвращает старое управляющее слово.

  • _EM_INVALID – исключение EXCEPTION_FLT_INVALID_OPERATION;
  • _EM_DENORMAL – исключение EXCEPTION_FLT_DENORMAL_OPERAND;
  • _EM_ZERODIVIDE – исключение EXCEPTION_FLT_DIVIDE_BY_ZERO;
  • _EM_OVERFLOW – исключение EXCEPTION_FLT_OVERFLOW;
  • _EM_UNDERFLOW – исключение EXCEPTION_FLT_UNDERFLOW;
  • _EM_INEXACT – исключение EXCEPTION_FLT_INEXACT_RESULT.

Ниже приведен пример программы, которая обрабатывает исключение с плавающей точкой при делении на ноль. Все это прекрасно работает в консольных приложениях, а вот добиться нормальной работы обработчика исключений с плавающей точкой в MFC-приложениях мне так и не удалось. Можно, конечно, заменить системный обработчик исключений, как это описано в п.1.4, но тогда программа будет завершаться при возникновении исключения (хотя и не аварийно, то есть без надоевшего всем вопроса Windows XP об отправке отчета в компанию Microsoft). В общем, пришлось мне для обработки исключений с плавающей точкой воспользоваться еще одним способом. Этот способ более трудоемок, зато работает. Хотя во многих случаях проще проверять значения с помощью оператора if, не прибегая к «хитромудрым» способам обработки исключений в Visual C++.

1.6. Использование блоков try и catch

2. Финальная обработка исключений

2.1. Финальные блоки фрейма

В операционных системах Windows существует еще один способ обработки исключений. При этом способе код, где возможно возникновение исключения, также заключается в блок __try. Но теперь за этим блоком следует блок __finally. В таком случае блок __finally выполняется всегда – независимо от того, произошло исключение или нет. Такой способ обработки исключений называется финальная обработка исключений. Структурно финальная обработка выглядит следующим образом: Финальная обработка исключений используется для того, чтобы при любом исходе исполнения блока __try освободить ресурсы (память, файлы и т.п.), которые были захвачены внутри этого блока.

Недостатком такого метода является то, что финальный код будет выполняться в любом случае. А это не всегда хорошо. Например, если мы пытаемся освободить память, которая распределяется в блоке __try, то это может привести к ошибке, если до распределения памяти дело не дошло (исключение произошло раньше). Чтобы избежать такой ситуации, нужно проверить, как завершился блок __try – нормально или нет.

2.2. Проверка завершения фрейма

  • Нормальное завершение блока.
  • Выход из блока при помощи управляющей инструкции __leave.
  • Выход из блока при помощи одной из управляющих инструкций return, break, continue или goto.
  • Передача управления обработчику исключения.

Чтобы определить, как завершился блок __try, используется функция AbnormalTermination, которая имеет следующий прототип: В случае если блок __try завершился ненормально, эта функция возвращает ненулевое значение, иначе – значение FALSE. Используя эту функцию, ресурсы, захваченные в блоке __try, можно освобождать в зависимости от ситуации. Пример:

Исключение указывает на состояние ошибки, возникающее при выполнении программы. Можно указать отладчику, какие исключения или наборы исключений должны вызывать прерывание и в какой момент нужно прервать выполнение (то есть приостановить отладчик). Когда отладчик прерывает работу, он показывает, где было создано исключение. Кроме того, можно добавлять или удалять исключения. После открытия решения в Visual Studio в разделе Отладка > Windows > Параметры исключений откройте окно Параметры исключений.

  • Создается исключение, которое не обрабатывается.
  • Отладчик настроен на прерывание выполнения до вызова обработчика.
  • Задан параметр Только мой код, и отладчик настроен на прерывание по любому исключению, не обрабатываемому в коде пользователя.

В приложениях, написанных на Visual Basic, отладчик управляет всеми ошибками как исключениями, даже при использовании обработчиков ошибок типа On Error.

Настройка отладчика для прерывания выполнения при создании исключения

Отладчик может прервать выполнение приложения в точке возникновения исключения, чтобы вы могли проверить исключение еще до вызова обработчика.

В окне Параметры исключений (Отладка > Windows > Параметры исключений) разверните узел для категории исключений, например Исключения среды CLR. Затем установите флажок для конкретного исключения в этой категории, например System.AccessViolationException. Можно также выбрать всю категорию исключений.

Отмечено AccessViolationException

Для поиска конкретных исключений можно воспользоваться окном Поиск на панели инструментов Параметры исключений или применить функцию поиска для фильтрации определенных пространств имен (например, System.IO).

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

Если исключение AccessViolationException отмечено в окне Параметры исключений, при выполнении этого кода в режиме отладчика произойдет останов на строке throw . После этого выполнение можно продолжить. В консоли должны отображаться обе строки.

Но в ней не отображается строка here .

Далее приводится метод Main() консольного приложения:

Если исключение AccessViolationException отмечено в окне Параметры исключений, при выполнении этого кода в режиме отладчика произойдет останов на строке throw в методах ThrowHandledException() и ThrowUnhandledException() .

Чтобы восстановить параметры исключений до значений по умолчанию, выберите Восстановить для списка параметры по умолчанию:

Восстановить параметры исключения до значений по умолчанию

Настройка отладчика для возобновления выполнения при возникновении не обработанных пользователем исключений

В окне Параметры исключений откройте контекстное меню, щелкнув правой кнопкой мыши метку столбца, а затем выберите Показать столбцы > Дополнительные действия. (Если параметр Только мой код отключен, данная команда не отображается.) Отобразится третий столбец с именем Дополнительные действия.

Столбец "Дополнительные действия"

Для исключения, у которого отображается Продолжить, если не обрабатывается в пользовательском коде в этом столбце, отладчик продолжает работу, если это исключение не обрабатывается в пользовательском коде, но обрабатывается в другом месте.

Чтобы изменить этот параметр для конкретного исключения, выберите исключение, щелкните правой кнопкой мыши, чтобы открыть контекстное меню, и выберите пункт Продолжить, если не обрабатывается в пользовательском коде. Вы также можете изменить параметр для всей категории исключений, например для всех исключений среды CLR.

Параметр Продолжить, если не обрабатывается в пользовательском коде

Прервать для необрабатываемых пользователем исключений

Добавление и удаление исключений

Исключения можно добавлять и удалять. Чтобы удалить тип исключения из категории, выберите исключение и нажмите кнопку Удалить выбранное исключение из списка (знак "минус") на панели инструментов Параметры исключений. Или щелкните исключение правой кнопкой мыши и выберите Удалить в контекстном меню. Удаление исключения аналогично снятию флажка для исключения и заключается в том, что при возникновении исключения отладчик продолжит выполнение.

В окне Параметры исключений выберите одну из категории исключений (например, Среда CLR).

Введите имя исключения (например, System.UriTemplateMatchException).

Введите имя исключения

Исключение будет добавлено в список (в алфавитном порядке) и будет автоматически выбрано.

Чтобы добавить исключение в категории "Исключения доступа к памяти GPU", "Исключения среды выполнения JavaScript" или "Исключения Win32", необходимо включить код ошибки, а также описание.

Проверьте правильность написания! В окне Параметры исключений не проверяется существование добавленного исключения. Поэтому при вводе Sytem.UriTemplateMatchException появится запись для этого исключения (а не для System.UriTemplateMatchException).

Параметры исключения сохраняются в файл SUO решения и таким образом применяются к конкретному решению. Параметры конкретного исключения нельзя повторно использовать в решениях. Сейчас сохраняются только добавленные исключения. Удаленные исключения не сохраняются. Вы можете добавить исключение, закрыть и повторно открыть решение — исключение будет находиться в нем по-прежнему. Однако при удалении исключения, закрытии и повторном открытии решения исключение появится снова.

Вы можете добавить исключение в окне Параметры исключений, используя предыдущую процедуру:

добавление общего исключения

Добавление условий в исключение

Используйте окно Параметры исключений, чтобы задать условия для исключений. В числе поддерживаемых условий есть имена модулей, что позволяет включить или исключить определенное исключение. При задании имен модулей в качестве условий можно приостановить выполнение на исключении только для определенных модулей кода. Вы также можете избежать прерывания в определенных модулях.

Добавление условий в исключение поддерживается, начиная с Visual Studio 2017.

Чтобы добавить условные исключения, выполните следующие действия.

Условия для исключения

Чтобы добавить дополнительное условие к исключению, выберите Добавить условие. Отобразятся строки дополнительные условий.

Дополнительные условия для исключения

Для каждой строки условия введите имя модуля и измените список операторов сравнения на Равно или Не равно. Можно указать подстановочные знаки ( \* ) в имени, чтобы выбрать более одного модуля.

Если необходимо удалить условие, выберите X в конце строки условия.

Пакеты VSPackage и COM используют одну и ту же архитектуру для ошибок. SetErrorInfo Функции и GetErrorInfo являются частью прикладного программного интерфейса (API) Win32. Любой пакет VSPackage в интегрированной среде разработки (IDE) может вызывать эти глобальные API-интерфейсы Win32 для записи подробных сведений об ошибках при получении уведомления об ошибке. SDK для Visual StudioПредоставляет сборки взаимодействия для управления сведениями об ошибках.

Методы взаимодействия

Как средство реализации VSPackage, объекты COM обычно реализуют ISupportErrorInfo . ISupportErrorInfo Интерфейс гарантирует, что подробные сведения об ошибках можно будет перемещать по цепочке вызовов по вертикали. Объекты, которые могут использоваться в процессах или в потоках, должны поддерживать ISupportErrorInfo , чтобы обеспечить правильную упаковку данных об ошибках обратно в вызывающий объект.

Все объекты, связанные с VSPackage и участвующие в расширении интегрированной среды разработки, включая фабрики, редакторы, иерархии и предлагаемые службы, должны поддерживать подробные сведения об ошибках. Хотя интегрированная среда разработки не требует реализации этих объектов VSPackage ISupportErrorInfo , всегда рекомендуется.

Интегрированная среда разработки отвечает за создание отчетов об ошибках и отображение их пользователю Visual Studio при каждом HRESULT распространении в интегрированную среду разработки. Интегрированная среда разработки также является механизмом для создания ErrorInfo объектов.

Общие рекомендации

Реализуйте ISupportErrorInfo в COM-объектах VSPackage.

Создайте механизм создания отчетов об ошибках, который вызывает SetErrorInfo метод в объектах, реализующих IOleCommandTarget .

Позвольте интегрированной среде разработки отображать ошибки пользователям с помощью ReportErrorInfo метода.

Сведения об ошибке в интегрированной среде разработки

Следующие правила указывают, как выполнять обработку сведений об ошибках в Visual Studio интегрированной среде разработки:

Любая сторона, которая явно игнорирует ошибку, HRESULT должна вызывать SetErrorInfo метод с S_OK . В противном случае ErrorInfo объект может быть случайно использован, когда другая сторона создает ошибку, не предоставляя собственной ErrorInfo .

Всем методам, которые поступили к ошибке HRESULT , рекомендуется вызывать SetErrorInfo метод для предоставления подробных сведений об ошибках. Если возвращаемое значение HRESULT является особой FACILITY_ITF ошибкой, метод необходим для предоставления соответствующего ErrorInfo объекта. Если возвращенная ошибка является стандартной системной ошибкой (например. E_OUTOFMEMORY , E_ABORT E_INVALIDARG E_UNEXPECTED и т. д.), допустимо возвращать код ошибки без явного вызова SetErrorInfo метода. В качестве стратегии защитного кода при возникновении ошибки HRESULT (включая системные ошибки) всегда вызывайте SetErrorInfo метод с ErrorInfo описанием ошибки более подробно или null .

Все функции, которые возвращают ошибку, вызванную другим вызовом, должны передавать сведения, полученные от неудачного вызова, в HRESULT без изменения ErrorInfo объекта.


Содержание статьи:

Вступление

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

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

В таблице ниже перечислим несколько свойств.

Свойство Функция
InnerException Выдача экземпляра класса, вызвавшего исключение
Source Указание на имя объекта, который вызвал исключение
Message Текст ошибки
HelpLink Ссылка на файл справки, связанный с исключениями.

Если нужно обработать только определенные ошибки, используют конкретный тип исключений.

Давайте рассмотрим несколько наиболее популярных.

Тип исключения Значение
ArgumenOutOfRangeException Значение аргумента не соответствует допустимому диапазону
IndexOutOfRangeException Выход за диапазон массива или допустимых значений
StackOverflowException Переполнение стека
OutOfMemoryException Недостаточно памяти для выполнения программы
NullReferenceException Обращение к неопределенному объекту

Обработку разных типов можно разграничить. Для этого каждый тип прописывают в отдельном блоке catch . Здесь важно помнить о приоритетности выполнения, так как предложения catch будут срабатывать в порядке написания.

Если менее конкретные исключения будут записаны перед более конкретными, то компилятор может выдать ошибку из-за невозможности достигнуть следующего блока.

Используя блоки try , catch и finally сначала выполняются команды в блоке try . Когда исключений нет, программа сразу переходит к блоку finally (если он есть) и часть программы, которая отвечает за обработку исключений, завершается.

Когда в блоке try произошла ошибка, то выполнение программы приостанавливается, а общеязыковая исполняющая среда (CLR) производит поиск блока catch . Если блок найден, то после его выполнения идет блок finally . В другом случае программа аварийно завершит работу.

Например, в случае деления на 0, возникает исключение System.DivideByZeroException . Чтобы не возникало таких ошибок, в блоке catch прописываем инструкцию, как указано в примере кода. Алгоритм не будет делить на 0 и выводить результаты, а программа аварийно завершается.

Ошибки в приложениях с визуальным интерфейсом

Все вышеуказанные правила работают в консольных приложениях. Если в программы есть интерфейс, то при возникновении ошибки увидим соответствующее окно:

Пример ошибки в Microsoft Visual Studio

Пример ошибки в Microsoft Visual Studio

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

Генерирование исключительных ситуаций

Фильтры и условные конструкции

Метод Int32.TryParse выдаст значение true , если тип возможно преобразовать. Таким образом для этого типа ошибок необязательно применять try / catch .

В заключение

Highload нужны авторы технических текстов. Вы наш человек, если разбираетесь в разработке, знаете языки программирования и умеете просто писать о сложном!
Откликнуться на вакансию можно здесь .

Читайте также: