IMHO.WS

IMHO.WS (https://www.imho.ws/index.php)
-   Программирование (https://www.imho.ws/forumdisplay.php?f=40)
-   -   C++Builder bug? (https://www.imho.ws/showthread.php?t=75515)

blacklist 11.12.2004 00:23

C++Builder bug
 
Имеется следующий, довольно таки незамысловатый, код:
Код:

  __try
  {
    int b = 0;
    int a = 10;
    ShowMessage(IntToStr(a/b));
  }
  catch(const EDivByZero &E)
  {
    ShowMessage(E.Message);
    if (E.ExceptionRecord)
      ShowMessage(IntToHex((int)E.ExceptionRecord->ExceptionCode,8));
    else
      ShowMessage("ExceptionRecord is NULL!");
  }

код в идеале должен перехватывать исключение, возбуждаемое в результате деления на ноль,
и выводить друг за другом два диалоговых окна: первое с сообщением "Division by zero",
второе с числом С0000094, которое соответствует коду STATUS_INTEGER_DIVIDE_BY_ZERO (как объявлено в файле winnt.h).
Однако вместо этого первое окно почему-то выводится с сообщением "EDivByZero", а второе окно
информирует, что поле ExceptionRecord в экземпляре класса исключения равно NULL.

Аналогичный код на Delphi работает "на ура" и выдаёт как раз то, что от него ожидается:
Код:

try
  b := 0;
  a := 10;
  ShowMessage(IntToStr(a div b));
 except
  on E: EDivByZero do begin
    ShowMessage(E.Message);
    if E.ExceptionRecord<>nil then
      ShowMessage(IntToHex(E.ExceptionRecord^.ExceptionCode,8))
    else
      ShowMessage('ExceptionRecord is NIL!');
  end;
 end;

А теперь, внимание вопрос ;)
Что это за глюк такой в компиляторе C++Builder'а :confused:

p.s.: версии -- C++Builder 6.0 Build 10.161 (Update 1), Delphi 6.0 Build 6.163, Windows 2000 Pro SP4

spike 11.12.2004 02:10

blacklist поставь update 4, может там что подправили

blacklist 11.12.2004 05:29

ok, попробую... как скачаю, напишу что получилось.

p.s.: ну и здоровый этот update 4 -- без малого 24 метра. Где ж они там столько багов отыскали? :rolleyes:

blacklist 13.12.2004 03:51

Скачал Update 4, установил и... ничего не изменилось :(

Народ, напишите pls, как у вас работает приведенный выше код для C++Builder'а. Может это только у меня такая проблема?

zhsa 16.12.2004 16:50

В пятом билдере то же самое, что и у тебя

blacklist 18.12.2004 06:16

Мда... Невесело... Ведь такой глюк проявляется при попытке перехватить исключение любого класса производного от EExternal - то есть всех исключений, которые возбуждаются операционной системой. Вот ещё пример для EAccessViolation (который, само-собой, работает неправильно):
Код:

{
  __try
  {
    int* p = NULL;
    *p = 0x1234;
  }
  catch(const EAccessViolation &E)
  {
    if (E.ExceptionRecord)
      ShowMessage(IntToHex((int)E.ExceptionRecord->ExceptionAddress,8));
    else ShowMessage("ExceptionRecord is NULL!");
  }
}

Так что для любых классов исключений, производных от EExternal, ни вывести оригинальное сообщение об ошибке, ни, тем более, адрес этой ошибки, похоже нельзя никак... Обидно... :(

p.s.: тех программеров, чьи светлые головы посетила идея использовать ExceptObject и ExceptAddr, спешу успокоить -- мою голову она посетила раньше. Что бы много не писать, скажу просто - НЕ работает (и ExceptAddr, и ExceptObject равно NULL).

ЕЖ 20.12.2004 14:51

blacklist
Вот тебе работа с AccessViolation, остальное по аналогии:
Код:

static int filter(EXCEPTION_POINTERS *xp)
{
  int return_code;
  EXCEPTION_RECORD *xr = xp->ExceptionRecord;

  switch (xr->ExceptionCode)
  {
    case EXCEPTION_ACCESS_VIOLATION:
      return_code = EXCEPTION_EXECUTE_HANDLER;
      ShowMessage("ExceptionCode: "+IntToStr(xr->ExceptionCode)+"\nExceptionAddress: "+IntToHex((int)xr->ExceptionAddress,8));
      break;

    default:
      return_code = EXCEPTION_CONTINUE_SEARCH;
      break;
  };
  return return_code;
}


void __fastcall TForm1::Button1Click(TObject *Sender)
{
  EXCEPTION_POINTERS *xp;
  try
  {
    int* p = NULL;
    *p = 0x1234;
  }
  __except (filter(xp=GetExceptionInformation()))
  {
    //...
  }
}


blacklist 25.12.2004 05:16

ЕЖ спасибо за помощь. Хоть через ж..у, зато работает на все сто. :cool:
Кстати, я локализировал место проблемы - это не в компиляторе глюк, а в RTL (конкретно в файле xx.cpp, функция getExceptionObject). Попробую перекомпилировать, если что получится напишу - может кому пригодится...

blacklist 12.01.2005 04:09

Fix
 
Ну вот, как говорится - не прошло и полмесяца... Да, праздники - классная штука ;)

Сначала немного эмоций: :)
1) такое ощущение, что модуль 'xx.cpp' писали с закрытыми глазами и при том левой ногой - настолько он кривой. Особенно мне понравился комментарий к одному отключённому блоку кода: 'The following code does NOT work correctly ... So for now this code is disabled again, till we figure out a better way to do this'. Как говорится - до лучших времён ;) . Ну да ладно, оставим это на совести автора.
2) Судя по ответу zhsa , этот баг присутствует и в предыдущих версиях RTL. Мне вот что интересно, как это он умудрился продержатся шесть версий Builder'а, и ни разу не был замечен (по крайней мере, на программерских форумах я подобных тем не нашёл). Что никто ExceptionRecord никогда не пробовал использовать? Или это так много народу C++Builder юзает?

Ну всё, хватит лирики. теперь о главном, собственно Fix:
итак правим файл $(BCB)\Source\Rtl\Source\except\xx.cpp
(Комментарий *** указывает на изменённую строку, а +++ -- на добавленною)

Функция getExceptionObject
Код:

static  excDscPtr      getExceptionObject(EXCEPTION_RECORD *P, unsigned long osEsp, unsigned long osERR, PCONTEXT ctx)
{
        int            ErrorCode;
        excDscPtr      xdp;
        tpidPtr        typeID;
        tpidPtr        baseID;
        EExternal *    excObj;                //  ***
        unsigned short  mask;
        ErrorCode = mapException(P);
        if      (ErrorCode < 3 || ErrorCode > sizeof(OSExceptionTypeIDs) / sizeof(OSExceptionTypeIDs[0]))
                return 0;
        typeID = OSExceptionTypeIDs[ErrorCode].OSETid;
        switch  (ErrorCode)
        {
        case    3:      excObj = new EDivByZero(Sysconst_SDivByZero)          ; break;        // ***
        case    4:      excObj = new ERangeError(Sysconst_SRangeError)        ; break;        // ***
        case    5:      excObj = new EIntOverflow(Sysconst_SIntOverflow)      ; break;        // ***
        case    6:      excObj = new EInvalidOp(Sysconst_SInvalidOp)          ; break;        // ***
        case    7:      excObj = new EZeroDivide(Sysconst_SZeroDivide)        ; break;        // ***
        case    8:      excObj = new EOverflow(Sysconst_SOverflow)            ; break;        // ***
        case    9:      excObj = new EUnderflow(Sysconst_SUnderflow)          ; break;        // ***
//      case    10:    excObj = new EInvalidCast("EInvalidCast")      ; break;
        case    11:    excObj = new EAccessViolation(                                // ***
                                  Sysconst_SAccessViolation,                                        // ***
                                  ARRAYOFCONST((                                                // ***
                                    P->ExceptionAddress,                                        // ***
                                    P->ExceptionInformation[0]?Sysconst_SWriteAccess:Sysconst_SReadAccess,          // ***
                                    (PVOID)P->ExceptionInformation[1])))      ; break;                // ***
        case    12:    excObj = new EPrivilege(Sysconst_SPrivilege)          ; break;        // ***
        case    13:    excObj = new EControlC(Sysconst_SControlC)            ; break;        // ***
        case    14:    excObj = new EStackOverflow(Sysconst_SStackOverflow)  ; break; // ***
        default:
          excObj = new EExternalException(Sysconst_SExternalException, ARRAYOFCONST(((int)P->ExceptionCode)));  // ***
        }

        mask = typeID->tpMask;
        assert(mask & TM_IS_REF);
        baseID = typeID->tpPtr.tppBaseType;
        xdp = allocExceptMem(sizeof(*xdp) + sizeof(EExternal *));                        // ***
        xdp->xdPrevious          = 0;
        xdp->xdTypeID    = typeID;
        xdp->xdFlags      = XDF_OSEXCEPTION;
        xdp->xdSize      = sizeof(EExternal *);                                        // ***
        xdp->xdMask      = mask;
        xdp->xdCflg      = 0;
        xdp->xdBase      = baseID;
        xdp->xdFriendList = (tpidPtr TPDST *)0;
        xdp->xdERRaddr    = 0;
        xdp->xdHtabAdr    = 0;
        xdp->xdFreeFunc  = freeExceptMem;
        xdp->xdThrowLine  = 0;
        xdp->xdThrowFile  = 0;
        xdp->xdCCaddr    = 0;
        xdp->xdCCmask    = 0;
        xdp->xdArgCopy    = 0;
        xdp->xdArgBuff    = 1;
        xdp->xdOSESP      = osEsp;
        xdp->xdOSERR      = osERR;
        xdp->xdOSContext  = ctx;
        excObj->ExceptionRecord = (Sysutils::PExceptionRecord)P;        // +++
        memcpy(xdp->xdValue, &excObj, sizeof(EExternal *));                // ***
        return xdp;
}

Функция __JumpToCatch__:
Код:

void    __cdecl __JumpToCatch__(void)
{
//  Commented setting of ESP - seems to be not needed, also it would prevent
//  our OS exception handling patch from working correctly, so ripped it...
//    __emit__(0x8B); __emit__(0xE3); /* mov esp, ebx */    // комментируем эту строку - зачем мы восстанавливаем
                                                      // значение ESP до выхода из обработчика для меня вообще загадка
    __emit__(0x8B); __emit__(0xE9); /* mov ebp, ecx */
    __emit__(0xFF); __emit__(0xE0); /* jmp eax      */
}

Теперь перекомпилируем RTL с помощью скрипта build.bat (он лежит в директории $(BCB)\Source\Rtl), не забыв перед этим добавить путь $(BCB)\Source\Rtl\Tools в путь поиска (например вот так: SET PATH=%PATH%;C:\CBUILDER\Source\Rtl\Tools).
Вот и всё, осталось только скопировать созданые в директории Source\Rtl\Lib файлы *.DLL и *.TDS в папку $(BCB)\Bin, а файлы *.LIB и *.OBJ в папку $(BCB)\Lib (попутно сохранив оригиналы - на всякий случай), запустить приведеный ранее пример и убедится что теперь всё работает правильно.

Ну что ж, вроде бы нигде не ошибся :), но если с новым RTL что-то начнёт глючить - пишите.

BkmzBIN 25.01.2005 17:53

blacklist - молодца! :claps:
огромный тебе RESPECT :beer:


Часовой пояс GMT +4, время: 14:41.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.