Здравствуйте, MT, Вы писали:

MT>Можно ли перехватить фатальные ошибки вроде "Memory could not be read"?

MT>Пробовал так:

MT>
MT>  try {

MT>  } catch (...) { 

MT>  }
MT>


MT>Но очевидно что не все ошибки перехватываются...


Рассказываю (и показываю). То что вы называете фатальными ошибками на самом деле являются SEH-исключениями операционной системы (внимательно читаем Рихтера). Они возбуждаются в различных ситуациях, как то: доступ по неправильному адресу, переполнение стека, деление на нуль, ошибка сопроцессора и.т.д. Будучи неперехваченным, SEH-исключение вызывает появление хорошо известного окна с предложением впаять разработчику и принудительной остановки процесса. Как перехватывать такие исключения в C++. Метод __try __except обсуждать не буду, поскольку его использование в C++ программах -- это дурной тон и возможный конфликт с родными C++ исключениями. Наиболее простой подход -- использовать catch(...). Для того, чтобы это работало, необходимо правильно настоить компилятор. Насчет VC++ 6.0 не помню, а вот в 7.0/7.1 придётся повыделываться. Заходим на вкладочку свойств проекта C/C++ Code Generation. Там есть строчка Enable C++ exception. Смело ставим в этой строчке No. Дальше двигаемся в конец к секции Command Line. На соотвествующей вкладочке есть окошко, Additional Options. Вот здесь надо прописать /EHac. Почему так, спросите у Билла Гейтса. Чтобы жизнь была интересней и разнообразней, наверное. Не всё ж галочки в property tab мышкой переключать. В чем недостаток этого подхода -- невозможно определить тип исключения, и ,что более серьёзно, возникают проблемы с плавающей арифметикой. Имеется более усовершенствованный метод. Заключается он в использовании так называемого se транслятора. Вот примерный код.


#include <eh.h>

static void se_translator(unsigned int code,_EXCEPTION_POINTERS *)
 {
  if( code==EXCEPTION_FLT_DENORMAL_OPERAND   ||
      code==EXCEPTION_FLT_DIVIDE_BY_ZERO     ||
      code==EXCEPTION_FLT_INEXACT_RESULT     ||
      code==EXCEPTION_FLT_INVALID_OPERATION  ||
      code==EXCEPTION_FLT_OVERFLOW           ||
      code==EXCEPTION_FLT_STACK_CHECK        ||
      code==EXCEPTION_FLT_UNDERFLOW
    )
    {
     short cw=???;        
        
     __asm { 
              fninit 
              fldcw  cw
           }
    }

  throw Exception(CPUError(code));
 }
 
void Setup_SE_translator()
 {
  _set_se_translator(se_translator);
 }


Смысл в следующем. Вызовом функции _set_se_translator можно инсталировать функцию, которая будет получать управление в случае возникновения в текущем потоке SEH-исключения. Главное назначение этой функции -- получить код SEH-исключения, завернуть в подходящую обёртку и выбросить нормальное C++ исключение. Которое в дальнейшем можно поймать обычным catch. Коды этих исключений можно получить из windows.h, а описание -- в MSDN в статье про EXCEPTION_RECORD. Среди этих кодов есть семейство особо важных, связанных с плавающей точкой. При получении одного из этих кодов, надо делать маленькую дополнительную обработку. А именно, нужно командой fninit сбросить сопроцессор в нормальное состояние. Ну и загрузить подходящее слово управление. Иначе флаги исключений по-прежнему будут висеть в сопроцессоре, что вызовет возбуждение нового исключения при попытке его снова заюзать. Вообще, использование исключений сопроцессора -- это отдельный нетривиальный вопрос. Поскольку с ним связан целый рад фокусов, на которые можно напороться.
... << RSDN@Home 1.1 beta 2 >>
Автор: Шахтер    Оценить