исключения и библиотеки (C++)

biven10

Ребят, а вот такой вопрос.
Есть приложение, вызывающее пару библиотек. Все на плюсах.
И вот очень редко приложение обваливается. При этом в eventlog пишется сообщение о возникшем исключении, но стандартное ошибочное окно не выскакивает (в котором еще предлагают либо отладить приложение, либо закрыть его).
Вопрос: значит ли это, что исключение возникло в библиотеке? Ведь если исключение не перехватывается в самом приложении, то по идее окошко это выскочить должно.
И еще - если, допустим, исключение возникло в библиотеке, то обязано ли приложение перехватить его методом catch (....) ?
Или, скажем, нужно использовать __try __except?

klyv

Твори, выдумывай, и, самое главное, - пробуй!
Написал бы, что за исключение.

Werdna

Вопрос: значит ли это, что исключение возникло в библиотеке?
Если библиотека кидает исключения, то это признак полной задницы в архитектуре. Я бы очень наказал того кто это делает, и отказался от использования такой библиотеки.
Библиотека не имеет права кидать исключения. Все ошибки — только по возврату.
ЗЫ: Я не имел в виду библиотеки типа набора шаблонных классов, как STL. Я имел в виду бинарные библиотеки, которые могли быть скомпилированы другим компилятором (версией и потому метод обработки исключений может привести к фатальностям.
Ведь если исключение не перехватывается в самом приложении, то по идее окошко это выскочить должно.

Окошка никакого не бывает, бывает остановка по 6 сигналу. Это в юниксовых, в винде наверное, да, окошко...

Werdna

Или, скажем, нужно использовать __try __except?
а что это за хрень?
Коллега, если ты пишешь на плюсах, пиши кросплатформенно, или пиши на своем до-диезе.

klyv

а что это за хрень?
Это - микрософтовая хрень для отлова исключений Win32. для С.

evolet

мне кажется, что если где-то исключение и кидается, то его кто-то ловит и просто грохает приложение.
Я бы на твоем месте попытался бы
1. отловить стек при убиваии приложения (terminate exit abort, ...)
2. отловить стек при записи сообщения в лог (надо смотреть, какое там у винды api для работы с eventlog'ом)
ну а дальше отлаживаться и думать :)

Andbar

Это - микрософтовая хрень для отлова исключений Win32. для С.
в смысле, для C++?

klyv

нет, для С.
The try-except statement is a Microsoft extension to the C language that enables applications to gain control of a program when events that normally terminate execution occur. Such events are called exceptions, and the mechanism that deals with exceptions is called structured exception handling.
Exceptions can be either hardware- or software-based. Even when applications cannot completely recover from hardware or software exceptions, structured exception handling makes it possible to display error information and trap the internal state of the application to help diagnose the problem. This is especially useful for intermittent problems that cannot be reproduced easily.
Хотя в С++ тоже :)

biven10

Исключение access violation
В лог пишется вот что:
Event Type: Error
Event Source: COM+
Event Category: SVC
Event ID: 4194
Date: 18.07.2008
Time: 15:40:29
User: N/A
Computer: CCAPPCORP
Description:
The system has called a custom component and that component has failed and generated an exception. This indicates a problem with the custom component. Notify the developer of this component that a failure has occurred and provide them with the information below.
Component Prog ID: SC.Pool 4 4
Method Name: IDispenserDriver::AddRef
Process Name: hdx_dc.exe
The serious nature of this error has caused the process to terminate.
Exception: C0000005
Address: 0x7878F3A2
Call Stack:
comsvcs!DispManGetContext(unsigned long *,unsigned long *) + 0x992
comsvcs!DispManGetContext(unsigned long *,unsigned long *) + 0x2181
и за ним:
Event Type: Error
Event Source: COM+
Event Category: SVC
Event ID: 4097
Date: 18.07.2008
Time: 15:40:29
User: N/A
Computer: CCAPPCORP
Description:
The run-time environment has detected an inconsistency in its internal state. Please contact Microsoft Product Support Services to report this error. *** Error in __FILE__(926): Application image dump failed.

evgen5555

EXCEPTION_ACCESS_VIOLATION - это к SEH относится, ловится __except'ом

evolet

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

evolet

м.б. у тебя такой случай ?
The system has called a custom component and that component has failed and generated an exception
:)
вы что, пишете комовские объекты, или библиотеки юзаете комовские ?
м.б. в одном из ваших комовских интрефейсных методов наружу вылетает AV ?
Если так, то не исключено , что некий код (некой библиотеки или системный, комовский) это исключение поймает (потому что там стоит catch(... запишет в лог и прибьет процесс, короче симптомы будут такие, как у тебя.

kokoc88

под виндой (во всяком случае после майкросовтофского компялтора (ну и для точности, под шестой студией - более позних я не пробовал) прекрасно поймается и с помощью catch(...)
Сейчас SEH ловится только если включена специальная опция. Но я не знаю таких идиотов, которые её сейчас включают.
Что касается темы, то там по всей видимости используется COM объект, который уже удалён. Видимо, кто-то забыл написать AddRef или неправильно назвал однопоточный КОМпонент многопоточным. Смысла ловить этот SEH в общем-то нет, после него всё равно придётся упасть.
Окошко программа не выводит, потому что кто-то инсталлировал SEH Handler, вызовы SetErrorMode, SetUnhandledExceptionFilter. SEH Handler записывает информацию о падении, сохраняет краш дампы, пишет лог и убивает программу. Скорее всего, это делает правильно настроенный стандартный механизм КОМов или механизм обработки ошибок в винде.
В любом случае можно поставить свой SEH Handler и упасть оттуда с сохранением краш дампа.

evolet

> Сейчас SEH ловится только если включена специальная опция. Но я не знаю таких идиотов, которые её сейчас включают.
таки мир становится лучше :) (давно не прогал под винду)
> то касается темы, то там по всей видимости используется COM объект, который уже удалён. Видимо, кто-то забыл
> написать AddRef или неправильно назвал однопоточный КОМпонент многопоточным.
мне кажется, скорее второе (или еще какой-то косяк многопоточности т.к. иначе падения были бы регулярными

evolet

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

Andbar

банально и AddRef забыт
а его вообще можно случайно забыть, юзая компоинтеры (или как там оно называется)?

evolet

можно, если смешивать CComPtr и голые указатели (а голыми указателями как минимум в параметрах и возвращаемых значениях комовских методов надо пользоваться например, надо не забывать в правильных местах Detach юзать (а то как раз лишний Release будет вызван)

biven10

Если так, то не исключено , что некий код (некой библиотеки или системный, комовский) это исключение поймает (потому что там стоит catch(... запишет в лог и прибьет процесс, короче симптомы будут такие, как у тебя.
Ну вот похоже оно и происходит. Лично я комовские объекты не исполдьзую. Скорее всего их юзает библиотека (сорсов и детального описания ее у меня нет).
А если так - то есть какой-нибудь способ? Сделать так, чтобы процесс не прибивался.

kokoc88

А если так - то есть какой-нибудь способ? Сделать так, чтобы процесс не прибивался.
Я же уже написал: SetErrorMode, SetUnhandledExceptionFilter. Дальше обрабатываешь SEH, двигаешь PI на другую команду и продолжаешь выполнение. Но смысла в этом почти наверняка не будет.

biven10

Я же уже написал: SetErrorMode, SetUnhandledExceptionFilter. Дальше обрабатываешь SEH, двигаешь PI на другую команду и продолжаешь выполнение. Но смысла в этом почти наверняка не будет.
упс
не до конца прочитала ответ
пасиб!
попробую...

evolet

Дальше обрабатываешь SEH, двигаешь PI на другую команду и продолжаешь выполнение
оооооооо, ну ты жесткий чувак :)
и это только для того (как я понял) заткнуть багу (или некорректное использование) библиотеки...
мне тут конечно подсказывают, что ты стебешся , но я все равно не могу удержаться
чтобы продвинуть EIP, нужно как минимум узнать длину текущей команды, а это правильно сделать ой как не просто :)
Я так и представил себе бедного (начинающего?) программиста(у пытающегося проделать (и отладить!) такой трюк :)

kokoc88

Тут спросили, как сделать, чтобы на этом месте программа не падала, и я ответил. :cool: Длину текущей команды можно узнать очень просто: свалить прогу в студии и посмотреть дизасм.
Спросили бы меня "что делать?", я бы сказал, чтобы сохранили краш дамп и посмотрели стек. А потом по обстоятельствам либо править свой код, либо искать поставщика КОМпонента.

biven10

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

Dasar

чтобы продвинуть EIP, нужно как минимум узнать длину текущей команды, а это правильно сделать ой как не просто
этого разве в seh-е нет?

evolet

я не знаю, я о них знаю только то, что они существуют :),
но вряд-ли (да из инфы там по-моему 1 или 2 DWORD'а, ну в общем мало т.к. бессмысленно делать что-либо после AV, отличное от убивания приложения. Во всяком случае в линуксе прямо заявлено, что если обработчик хардварных сигналов (сегфолт, например) возвращает управление, то что дальше будет - зависит от реализации, напрмер, система сама может убить процесс.

Dasar

да из инфы там по-моему 1 или 2 DWORD'а, ну в общем мало
инфы, там до хрена. как минимум состояния всех регистров - включая mmx и fpu
зы
посмотрел.
да, адреса следующей команды к сожалению там нет.

evolet

да, инфы там (во всяком случае изначально, не знаю, можно ли ее в __try _except выцепить) ровно столько (ооо половина чего совсем непонятного)

EXCEPTION_DISPOSITION
__cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);

в том числе struct _CONTEXT

причем прикольно в винде, контекст можно ручками подхачить(возможно, что даже eip можно подменить) и сказать "теперь все ОК, продолжай выполнение", прикол...

klyv

и сказать "теперь все ОК, продолжай выполнение"
Думаешь, как On Error Resume Next работает?

evolet

не понял о чем ты :)
на http://www.wasm.ru/article.php?article=Win32SEHPietrek1
сказано, что такой код работает (я конечно сам не проверял..., но думаю, что там вряд ли обманывают :) )

EXCEPTION_DISPOSITION
__cdecl
_except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext )
{
unsigned i;

// Сообщаем, что сработал наш обработчик исключений.
printf( "Hello from an exception handler!\n" );

// Изменяем значение регистра EAX в context record таким образом, чтобы оно
// указывало на какое-либо, доступное для записи место в памяти.
ContextRecord->Eax = (DWORD)&scratch;

// Просим ОС еще раз попытаться выполнить вызвавшую исключение инструкцию.
return ExceptionContinueExecution;
}

klyv

не понял о чем ты :)
в Visual Basic есть такой вариант обработки ошибок - забивать на них и идти далее выполнять прогу.

Andbar

причем прикольно в винде, контекст можно ручками подхачить(возможно, что даже eip можно подменить) и сказать "теперь все ОК, продолжай выполнение", прикол...
а что, у нас так завершают подвисшие регрешнтесты... :o Правда, выполнение этого в отладчике, как правило, приводит к вываливанию приложения. А так, если дело идёт к завершению приложения, а какой-то поток подвис (если это, конечно, не связано с каким-то вводом-выводом то изменив EIP можно попытаться корректно освободить потоко-зависимые ресурсы (если в этом есть какой-либо смысл).
Оставить комментарий
Имя или ник:
Комментарий: