| imho.ws |
![]() |
|
|
|
# 1 |
|
Newbie
Регистрация: 25.08.2004
Сообщения: 11
![]() |
Имеется следующий, довольно таки незамысловатый, код:
Код:
__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. |
|
|
|
|
# 6 |
|
Newbie
Регистрация: 25.08.2004
Сообщения: 11
![]() |
Мда... Невесело... Ведь такой глюк проявляется при попытке перехватить исключение любого класса производного от 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!");
}
}
p.s.: тех программеров, чьи светлые головы посетила идея использовать ExceptObject и ExceptAddr, спешу успокоить -- мою голову она посетила раньше. Что бы много не писать, скажу просто - НЕ работает (и ExceptAddr, и ExceptObject равно NULL). |
|
|
|
|
# 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()))
{
//...
}
}
|
|
|
|
|
# 8 |
|
Newbie
Регистрация: 25.08.2004
Сообщения: 11
![]() |
ЕЖ спасибо за помощь. Хоть через ж..у, зато работает на все сто.
Кстати, я локализировал место проблемы - это не в компиляторе глюк, а в RTL (конкретно в файле xx.cpp, функция getExceptionObject). Попробую перекомпилировать, если что получится напишу - может кому пригодится... |
|
|
|
|
# 9 |
|
Newbie
Регистрация: 25.08.2004
Сообщения: 11
![]() |
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;
}
Код:
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 */
}
Вот и всё, осталось только скопировать созданые в директории Source\Rtl\Lib файлы *.DLL и *.TDS в папку $(BCB)\Bin, а файлы *.LIB и *.OBJ в папку $(BCB)\Lib (попутно сохранив оригиналы - на всякий случай), запустить приведеный ранее пример и убедится что теперь всё работает правильно. Ну что ж, вроде бы нигде не ошибся , но если с новым RTL что-то начнёт глючить - пишите.
|
|
|