imho.ws
IMHO.WS  

Вернуться   IMHO.WS > Компьютеры > Программирование
Опции темы
Старый 11.12.2004, 00:23     # 1
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

blacklist Путь к славе только начался
Question 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'а

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

Последний раз редактировалось blacklist; 12.01.2005 в 04:13.
blacklist вне форума  
Старый 11.12.2004, 02:10     # 2
spike
Member
 
Аватар для spike
 
Регистрация: 02.09.2003
Сообщения: 215

spike Молодецspike Молодецspike Молодец
blacklist поставь update 4, может там что подправили
__________________
не могу просто так сидеть, когда другие работают. пойду полежу.
spike вне форума  
Старый 11.12.2004, 05:29     # 3
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

blacklist Путь к славе только начался
ok, попробую... как скачаю, напишу что получилось.

p.s.: ну и здоровый этот update 4 -- без малого 24 метра. Где ж они там столько багов отыскали?
blacklist вне форума  
Старый 13.12.2004, 03:51     # 4
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

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

Народ, напишите pls, как у вас работает приведенный выше код для C++Builder'а. Может это только у меня такая проблема?
blacklist вне форума  
Старый 16.12.2004, 16:50     # 5
zhsa
Guest
 
Сообщения: n/a

В пятом билдере то же самое, что и у тебя
 
Старый 18.12.2004, 06:16     # 6
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

blacklist Путь к славе только начался
Мда... Невесело... Ведь такой глюк проявляется при попытке перехватить исключение любого класса производного от 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).
blacklist вне форума  
Старый 20.12.2004, 14:51     # 7
ЕЖ
::VIP::
 
Регистрация: 19.03.2004
Сообщения: 1 329

ЕЖ Бог с наворотамиЕЖ Бог с наворотами
ЕЖ Бог с наворотамиЕЖ Бог с наворотами
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()))
  {
    //...
  }
}
ЕЖ вне форума  
Старый 25.12.2004, 05:16     # 8
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

blacklist Путь к славе только начался
ЕЖ спасибо за помощь. Хоть через ж..у, зато работает на все сто.
Кстати, я локализировал место проблемы - это не в компиляторе глюк, а в RTL (конкретно в файле xx.cpp, функция getExceptionObject). Попробую перекомпилировать, если что получится напишу - может кому пригодится...
blacklist вне форума  
Старый 12.01.2005, 04:09     # 9
blacklist
Newbie
 
Регистрация: 25.08.2004
Сообщения: 11

blacklist Путь к славе только начался
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 что-то начнёт глючить - пишите.
blacklist вне форума  
Старый 25.01.2005, 17:53     # 10
BkmzBIN
Banned
 
Аватар для BkmzBIN
 
Регистрация: 25.01.2005
Сообщения: 1

BkmzBIN Нуль без палочки
Thumbs up

blacklist - молодца!
огромный тебе RESPECT
BkmzBIN вне форума  

Опции темы

Ваши права в разделе
Вы НЕ можете создавать новые темы
Вы не можете отвечать в темах.
Вы НЕ можете прикреплять вложения
Вы НЕ можете редактировать свои сообщения

BB код Вкл.
Смайлы Вкл.
[IMG] код Выкл.
HTML код Выкл.

Быстрый переход


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




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