OpenGL подавляет исключения в диалоговом приложении MFC

У меня есть диалоговое приложение, управляемое MFC, созданное с помощью MSVS2005. Вот моя проблема шаг за шагом. У меня есть кнопка в моем диалоговом окне и соответствующий обработчик кликов с таким кодом:

int* i = 0;
*i = 3;

Я запускаю отладочную версию программы, и когда я нажимаю кнопку, Visual Studio перехватывает фокус и предупреждает об исключении «Место записи нарушения доступа», программа не может восстановиться после ошибки, и все, что я могу сделать, это остановить отладку. И это правильное поведение.

Теперь я добавляю код инициализации OpenGL в метод OnInitDialog():

    HDC DC = GetDC(GetSafeHwnd());
    static PIXELFORMATDESCRIPTOR pfd =
    {
      sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
      1, // version number
      PFD_DRAW_TO_WINDOW | // support window
      PFD_SUPPORT_OPENGL | // support OpenGL
      PFD_DOUBLEBUFFER, // double buffered
      PFD_TYPE_RGBA, // RGBA type
      24, // 24-bit color depth
      0, 0, 0, 0, 0, 0, // color bits ignored
      0, // no alpha buffer
      0, // shift bit ignored
      0, // no accumulation buffer
      0, 0, 0, 0, // accum bits ignored
      32, // 32-bit z-buffer
      0, // no stencil buffer
      0, // no auxiliary buffer
      PFD_MAIN_PLANE, // main layer
      0, // reserved
      0, 0, 0 // layer masks ignored
    };

    int pixelformat = ChoosePixelFormat(DC, &pfd);
    SetPixelFormat(DC, pixelformat, &pfd);

    HGLRC hrc = wglCreateContext(DC);
    ASSERT(hrc != NULL);
    wglMakeCurrent(DC, hrc);

Конечно, это не совсем то, чем я занимаюсь, это упрощенная версия моего кода. Что ж, теперь начинают происходить странные вещи: с инициализацией все в порядке, в OnInitDialog() ошибок нет, но когда я нажимаю кнопку... никаких исключений не выдается. Ничего не произошло. Вообще. Если я установлю точку останова на *i = 3; и нажму на ней F11, функция-обработчик немедленно остановится, и фокус вернется к приложению, которое продолжит работать хорошо. Я могу нажать кнопку еще раз, и произойдет то же самое.

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

Если я прокомментирую строку wglMakeCurrent(DC, hrc);, все работает нормально, как и раньше, генерируется исключение, и Visual Studio его перехватывает и показывает окно с сообщением об ошибке, после чего программа должна быть завершена.

Я сталкиваюсь с этой проблемой под Windows 7 64-бит, NVIDIA GeForce 8800 с установленными последними драйверами (от 11.01.2010), доступными на сайте. У моего коллеги 32-битная Windows Vista, и у него нет такой проблемы - выбрасывается исключение и приложение вылетает в обоих случаях.

Что ж, надеюсь, добрые ребята мне помогут :)

PS Первоначально проблема была опубликована в этой теме.


person Mikhail    schedule 29.01.2010    source источник
comment
Забавно, недавно у меня была та же проблема... если вы поместите код, вызывающий исключение, в блок try-catch, он перехватит исключение там, но если исключение распространяется в код MFC, оно, похоже, проглатывается. Я предполагаю, что потенциальным обходным путем является использование блоков try-catch самостоятельно.   -  person AshleysBrain    schedule 29.01.2010
comment
Я могу подтвердить эту проблему, и она связана с OpenGL в 64-разрядной версии Windows Vista и 64-разрядной версии Windows 7. Сам ищу ответ :)   -  person ralphtheninja    schedule 12.02.2010
comment
Это похоже на мою проблему: stackoverflow.com/questions/2622200/   -  person Mark Ingram    schedule 12.04.2010


Ответы (4)


Хорошо, я узнал немного больше информации об этом. В моем случае это Windows 7, которая устанавливает KiUserCallbackExceptionHandler в качестве обработчика исключений, прежде чем вызывать мой WndProc и давать мне управление выполнением. Это делается с помощью ntdll!KiUserCallbackDispatcher. Я подозреваю, что это мера безопасности, предпринятая Microsoft для предотвращения взлома SEH.

Решение состоит в том, чтобы обернуть ваш wndproc (или hookproc) фреймом try/except, чтобы вы могли перехватить исключение до того, как это сделает Windows.

Благодаря Skywing на http://www.nynaeve.net/

Мы связались с nVidia по поводу этой проблемы, но они говорят, что это не их ошибка, а ошибка Microsoft. Не могли бы вы рассказать, как вы нашли обработчик исключений? И есть ли у вас дополнительная информация, например. некоторые отзывы от Microsoft?

Я использовал команду "!exchain" в WinDbg, чтобы получить эту информацию.

person ralphtheninja    schedule 18.02.2010
comment
Мы связались с nVidia по поводу этой проблемы, но они говорят, что это не их ошибка, а ошибка Microsoft. Не могли бы вы рассказать, как вы нашли обработчик исключений? И есть ли у вас дополнительная информация, например. некоторые отзывы от Microsoft? - person Mikhail; 25.05.2010

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

http://msdn.microsoft.com/en-us/library/ms679274.aspx

person Mark Ingram    schedule 14.04.2010
comment
Я беру это обратно. Нам пришлось отменить это изменение, потому что оно было слишком агрессивным. Были исключения, которые мы хотели оставить в покое и позволить обрабатываться обычным способом, но мы не могли отличить их от нарушений прав доступа и тому подобного, которые мы хотим сломать. - person Brian; 17.12.2011
comment
У меня есть базовый класс для всего пользовательского интерфейса, который переопределяет WndProc с помощью __try/__catch. Затем обработчик вызывает отчет о сбое в выпуске, в отладке он продолжается, чтобы мы могли отладить сбой. - person Mark Ingram; 18.12.2011
comment
После дальнейшего расследования мы обнаружили, что наша проблема на самом деле была ошибкой Windows с 32-разрядными программами, работающими в 64-разрядных операционных системах. Об этом есть отдельная статья о переполнении стека, и я думаю, что это было настоящей проблемой и для исходного постера. - person Brian; 11.01.2012

Во-первых, оба поведения правильны. Разыменование нулевого указателя является «неопределенным поведением», а не нарушением гарантированного доступа.

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

Если вы настроите Visual Studio на остановку при нарушениях доступа первого шанса, не сломается ли он?

Вызовите VirtualQuery(NULL,...) до и после glMakeCurrent и сравните. Возможно, драйверы nVidia OpenGL содержат нулевую страницу VirtualAlloc (плохая идея, но не невозможная или незаконная).

person Ben Voigt    schedule 15.02.2010
comment
Такое же поведение для любых исключений, в том числе вручную инициированных с помощью оператора throw. Если я настрою Visual Studio так, чтобы он останавливался на исключениях первого шанса, он сломается. - person Mikhail; 16.02.2010
comment
comment
При сравнении результатов VirtualQuery(NULL,...) результаты одинаковы для вызовов до и после wglCreateContext(). - person Mikhail; 18.02.2010
comment
Предположение VirtualQuery было предназначено для устранения неполадок, если исключения нулевого указателя не срабатывали, а другие исключения срабатывали. Поскольку все исключения проглатываются, выполните векторные обработчики исключений. Вы должны, по крайней мере, получить некоторое представление о том, какие библиотеки DLL содержат обработчики и почему поведение этих двух систем отличается. - person Ben Voigt; 18.02.2010
comment
Я не уверен, что это так. Я думаю, что комментарий Марка Ингрэма был ближе к истине. Векторная обработка исключений кажется решением, а не проблемой. У меня была такая же проблема, и я добавил обработчик, который выдает appfatal и заставляет его падать при исключениях. Прежде чем добавить, что мне не удалось перейти к векторной обработке исключений. Как только я добавил один, я смог войти в него, что заставило меня поверить, что мой обработчик — единственный, и обработка сбоя — правильный способ добиться такого поведения. - person Brian; 07.12.2011

Я нашел этот вопрос, когда искал аналогичную проблему. Нашей проблемой оказалось молчаливое потребление исключений при запуске 32-битного приложения в 64-битной Windows.

http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages

Microsoft предлагает исправление, хотя его развертывание несколько затруднительно, если у вас несколько целевых платформ:

http://support.microsoft.com/kb/976038

Вот статья на эту тему, описывающая поведение:

http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/

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

person Brian    schedule 10.01.2012