Почему мой UnhandledExceptionFilter не вызывается при простом делении на ноль?

Я использую следующий код консольного приложения MSVC++ (работающий в Windows 8.1, Release, Win32), чтобы попытаться вернуть мне исключение верхнего уровня:

#include "stdafx.h"
#include <Windows.h>
#include <iostream>

using namespace std; 

LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
    printf("Got an unhandled exception.");
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    SetUnhandledExceptionFilter(UnhandledExceptionFilter);
    int a = 0;
    int b = 0;
    int c = a / b;
    cin.get();
    return 0;
}

Мой UnhandledExceptionFilter на самом деле, похоже, никогда не вызывается при исключении деления на ноль, которое выдается в этом примере, иначе я ожидал бы появления сообщения журнала «Получено необработанное исключение». Это почему?


person Alexandru    schedule 18.03.2014    source источник
comment
У меня было подобное столкновение имени между именем класса и именем, скрытым в заголовочном файле windows.h. Мое решение состояло в том, чтобы немного переименовать мой класс, чтобы он больше не сталкивался с именами классов windows.h. Из сообщений компилятора не всегда было очевидно, что содержимое windows.h использовалось первым.   -  person CPlusPlus OOA and D    schedule 19.03.2014
comment
Предоставленный вами пример не может быть успешно собран, потому что UnhandledExceptionFilter уже определено в kernel32.lib при связывании.   -  person zhenguoli    schedule 12.09.2019


Ответы (5)


Целочисленное деление на ноль является неопределенным поведением. Если реализация решит вызвать исключение для этого (или для любого другого неопределенного поведения), это нормально. Если реализация выбирает не для создания исключения для некоторого неопределенного поведения, это тоже нормально. Что бы ни делала реализация, когда сталкивалась с неопределенным поведением, это нормально. Это не их проблема.

По-видимому, MSVC++, работающий в Windows 8.1, Win32 не вызывает исключения при делении на ноль.

Вы никогда не должны ожидать, что неопределенное поведение приведет к чему-то ожидаемому.

person David Hammen    schedule 18.03.2014
comment
Есть ли способ переопределить это поведение, чтобы поймать это? - person Alexandru; 19.03.2014
comment
Также есть вероятность, что компилятор оптимизирует эту часть кода — я знаю, что он делает это для меня даже с -O1 (на MinGW) - person Tomer Arazy; 19.03.2014
comment
@Alexandru - В общем, нет. Это неопределенное поведение. Решение проблем неопределенного поведения состоит в том, чтобы вообще никогда не вызывать их. - person David Hammen; 19.03.2014
comment
Как я вижу, вы используете Windows... Windows поддерживает __try/__except/__finally, которые могут перехватывать эти типы исключений. Настоятельно рекомендуется предотвращать Div-0, а не ловить его. - person Chris; 19.03.2014
comment
Настоятельно рекомендуется предотвращать Div-0, а не ловить его. Да. То же самое касается сложения самого большого возможного целого числа со знаком, извлечения квадратного корня из отрицательного числа или вычисления asin(2). Просто не делай этого. - person David Hammen; 19.03.2014
comment
Интересно... какие типы необработанных исключений перехватывает фильтр необработанных исключений? Наверняка должна быть ссылка на них, не так ли? Я просто хочу убедиться, что поймал как можно больше в своем коде. :) - person Alexandru; 19.03.2014
comment
Причина, по которой я задаю этот вопрос, больше связана с этой целью (интересно прочитать, если вам, ребята, интересно, как создать трассировку стека для исключения, но я все еще пытаюсь настроить его, чтобы он работал так, как я хочу ): stackoverflow.com/questions/22487887/ - person Alexandru; 19.03.2014
comment
Ответ правильный, но в данном случае именно @TomerArazy правильно диагностировал проблему OP: в Release сборках оптимизация включена, а переменная c не используется. Компилятор пропустит его и выражение, которое его инициализирует — а также две строки над ним — и деления в коде не будет. Он мог бы попробовать написать c в консоль: Тогда он не будет мертвым и деление будет оценено. - person davidbak; 21.03.2018

Функция, указанная с помощью SetUnhandledExceptionFilter(), не будет вызываться, если приложение работает под отладчиком, идея состоит в том, что SetUnhandledExceptionFilter() следует использовать только для создания аварийного дампа и последующего выхода из приложения. Это не очень полезно, если у вас подключен отладчик, поэтому отладчик сначала обработает исключение.

документация MSDN ссылается на это:

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

person masrtis    schedule 18.03.2014
comment
Обратите внимание, что если вы используете что-то вроде ntsd, cdb или windbg из пакета Debugging Tools for Windows, вы можете управлять этим с помощью семейства команд sx*, управляющих тем, как исключения будут обрабатываться в отладчике. По умолчанию он сломается в отладчике. - person Michael Burr; 19.03.2014

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

person Cahit Gungor    schedule 19.03.2014
comment
Так же как и нарушение прав доступа, но оно надежно улавливается структурированной обработкой исключений Windows. - person MSalters; 19.03.2014
comment
Исключения, которые определены, как вы видите, деление на ноль не включены. Дополнительные сведения о реализации Microsoft C++ см. здесь. us/library/t65b74ad.aspx). Реализация GCC аналогична. domain_error Класс invalid_argument Класс length_error Класс logic_error Класс out_of_range Класс overflow_error Класс range_error Класс runtime_error Класс underflow_error Класс - person Cahit Gungor; 19.03.2014
comment
В этом вопросе мы говорим о программировании для Windows, и ОП спрашивает о конкретном API: SetUnhandledExceptionFilter. Структурированная обработка исключений — это игра. - person davidbak; 21.03.2018

Мне кажется, что исключения на самом деле не являются настоящим необработанным исключением с точки зрения ОС. Вы звоните printf, так что вы связались в CRT. IIRC, CRT обрабатывает исключения, выходящие из main, но с точки зрения ОС необработанное исключение — это исключение, выходящее за пределы истинной точки входа, то есть функции CRT, которая вызывает main.

Я считаю, что вам действительно нужен обработчик векторных исключений.

person MSalters    schedule 19.03.2014

  1. как говорит @zhenguoli, UnhandledExceptionFilter определено в kernel32.lib, поэтому оно не будет ссылаться. Вам нужно назвать свой фильтр исключений как-то иначе. UnhandledExceptionFilter - это то, что вызывает ваш обработчик исключений, установленный SetUnhandledExceptionFilter.

  2. @DavidHammen неясен, потому что деление на ноль действительно обрабатывается с вектором исключения 0 на x86, а GetExceptionCode() возвращает EXCEPTION_INT_DIVIDE_BY_ZERO. Но, как говорит @davidbak, в примере OP компилятор не позволяет этого, поскольку переменная c не используется и будет оптимизирована. Если бы использовалось c, то обработчик исключений в векторе 0 начал бы раскручивать стек и, в конце концов, вызвал бы UnhandledExceptionFilter в качестве выражения фильтра, которое обернуто вокруг функции входа потока в RtlUserThreadStart, которая затем вызовет ваш обработчик (поток запускается с RtlUserThreadStart как функция входа с функцией входа реального потока в качестве параметра). Функция входа .exe инициализирует ЭЛТ, а затем вызывает main. Вы правы, используя SEH, потому что divbyzero не может быть перехвачен обработкой исключений C++, которая отвечает только на throw.

person Lewis Kelsey    schedule 12.01.2020