Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>Здравствуйте, blackhearted, Вы писали:
ТКС>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
Здравствуйте, ononim, Вы писали:
O>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?
dll к тому,что — это подключаемые модули, которые нет смысла отдавать статически влинкованными всем клиентам.
В качестве замены предлагается статичеки компилировать разные версии для разных клиентов?
Здравствуйте, ononim, Вы писали:
O>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?
На времени линковки можно сильно сэкономить, в случае разных dll.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Kh_Oleg, Вы писали:
K_O>Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем.
Посмотрел на определение std::exception — оно содержит __declspec(dllimport), во всяком случае, если компилировать с /MD(d).
Так что vtable должно жить в msvcrt(d).dll
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, blackhearted, Вы писали:
B>>dll бросает исключение. B>>Его ловит Dummy::go() и в catch выгружает dll (в реальном приложении выгрузка происходит в другом потоке ниже по стеку,но результат тот же).
GN>Можно посмотреть на реальный вариант, включая call-stack?
try {
component->init();
} catch (const Exception& ex) {
log->fatal("failed to initialize: "
+ ex.what());
component->detach();
component->shutdown();// тут уходит в другой поток, который делает FreeLibrary throw;
}
***.exe!_NMSG_WRITE(int rterrnum=10) Line 198 C
***.exe!abort() Line 59 + 0x7 bytes C
***.exe!_wassert(const wchar_t * expr=0x04e33520, const wchar_t * filename=0x04e334e0, unsigned int lineno=448) Line 212 C
***.exe!`anonymous namespace'::trans_func(unsigned int u=3221225477, _EXCEPTION_POINTERS * ptr=0x062acf4c) Line 448 + 0x18 bytes C++
***.exe!_CallSETranslator(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, void * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 701 + 0xd bytes C++
***.exe!FindHandlerForForeignException(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, _CONTEXT * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, int curState=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 934 + 0x21 bytes C++
***.exe!FindHandler(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, _CONTEXT * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, unsigned char recursive=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 866 + 0x25 bytes C++
***.exe!__InternalCxxFrameHandler(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, _CONTEXT * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000, unsigned char recursive=0) Line 524 + 0x25 bytes C++
***.exe!__CxxFrameHandler3(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, void * pContext=0x062ad194, void * pDC=0x062ad14c) Line 365 + 0x1f bytes C++
ntdll.dll!ExecuteHandler2@20() + 0x26 bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!_KiUserExceptionDispatcher@8() + 0xe bytes
***.exe!FindHandler(EHExceptionRecord * pExcept=0x062ad5fc, EHRegistrationNode * pRN=0x062aff48, _CONTEXT * pContext=0x062ad61c, void * pDC=0x062ad5d0, const _s_FuncInfo * pFuncInfo=0x0545ac24, unsigned char recursive=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 740 + 0x6 bytes C++
***.exe!__InternalCxxFrameHandler(EHExceptionRecord * pExcept=0x062ad5fc, EHRegistrationNode * pRN=0x062aff48, _CONTEXT * pContext=0x062ad61c, void * pDC=0x062ad5d0, const _s_FuncInfo * pFuncInfo=0x0545ac24, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000, unsigned char recursive=0) Line 524 + 0x25 bytes C++
***.exe!__CxxFrameHandler3(EHExceptionRecord * pExcept=0x062ad5fc, EHRegistrationNode * pRN=0x062aff48, void * pContext=0x062ad61c, void * pDC=0x062ad5d0) Line 365 + 0x1f bytes C++
ntdll.dll!ExecuteHandler2@20() + 0x26 bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!_KiUserExceptionDispatcher@8() + 0xe bytes
kernel32.dll!_RaiseException@16() + 0x52 bytes
09627080()
Здравствуйте, blackhearted, Вы писали:
B>Здравствуйте, ononim, Вы писали:
O>>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?
B>dll к тому,что — это подключаемые модули, которые нет смысла отдавать статически влинкованными всем клиентам. B>В качестве замены предлагается статичеки компилировать разные версии для разных клиентов?
Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю. Хорошим примером, когда система может выгружать плагины, является Far. Неслучайно поэтому весь Plugin API у него — на С.
Пока посмотрю колстек, но вот идея, мне кажется должно работать:
class Dummy
{
public:
bool go(Library* lib)
{
std::cout<<"Dummy::go()"<<std::endl;
std::exception const* pex = goimpl(lib);
if ( pex )
{
std::cout<<"Rethrowing..."<<std::endl;
std::exception ex(*pex);
delete pex;
throw ex;
}
return false;
}
std::exception const* goimpl(Library* lib)
{
std::cout<<"Dummy::go()"<<std::endl;
try
{
lib->doAction();
}
catch(const std::exception& ex)
{
std::cout<<"Exception occured..."<<std::endl;
std::cout<<ex.what()<<std::endl;
loader.unload();
std::exception const* ce = new std::exception(ex); // но нужно обезопаситься от bad_allocreturn ce;
}
}
};
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Kh_Oleg, Вы писали:
K_O>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.
Даже если не бросаются исключения?
Как же все на плюсах пишут тогда приложения с плагинами?
Здравствуйте, blackhearted, Вы писали:
B>Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>>Здравствуйте, blackhearted, Вы писали:
ТКС>>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
B>Уже порушен — dll выгружена.
B>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Kh_Oleg, Вы писали:
K_O>>Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем.
К>Посмотрел на определение std::exception — оно содержит __declspec(dllimport), во всяком случае, если компилировать с /MD(d). К>Так что vtable должно жить в msvcrt(d).dll
__declspec(dllimport) всего лишь делает класс exception экспортируемым из DLL, т.е. видимым в таблице экспорта. Плюс шаманства с декорированием имен. И все.
Зато сам класс описан в заголовочном файле, а это значит, что копии описания такого класса (а конкретно — VMT) будут в каждом модуле (dll или exe), при компиляции которого был включен #include <exception>. В нашем случае — в exe и в dll. И у каждого модуля — своя копия VMT.
В данном случае идет попытка обратиться к той копии, которая выгружена благодаря FreeLibrary. Я вот только не пойму, зачем понадобилось выгружать. Очень не всегда это вообще возможно.
GN> но нужно обезопаситься от bad_alloc
А также, проверить, чтобы конструктор не вызывал виртуальных методов у ex.
А конструктор, как оказывается, обращается к полю _m_what (напрямую), которое const char* и копирует его себе. Так что тут все будет зависеть от того, куда указывает строка в "выгруженной" exception.
Здравствуйте, blackhearted, Вы писали:
B>Здравствуйте, Kh_Oleg, Вы писали:
K_O>>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.
B>Даже если не бросаются исключения? B>Как же все на плюсах пишут тогда приложения с плагинами?
А ты покажи мне стабильное приложение с плагинами на плюсах. Я таких не знаю. Far — написан на С++ и работает с плагинами, НО: интерфейс к плагинам у него — чистый С.
B>try {
B> component->init();
B> } catch (const Exception& ex) {
log->>fatal("failed to initialize: "
B> + ex.what());
B> component->detach();
B> component->shutdown();// тут уходит в другой поток, который делает FreeLibrary
B> throw;
B> }
B>
Так, тут картина довольно запутанная, но впринципе тоже самое, что и раньше. Дучаю, что другой тред выгруджает длл раньше чем завершаться раскрутка throw;. AV в FindHandler нет, потому что по-видимому в приложении установлен SE translator, вот он и пытается транслировать исключение и валится на внутреннем assert B>
B>***.exe!_NMSG_WRITE(int rterrnum=10) Line 198 C
B>***.exe!abort() Line 59 + 0x7 bytes C
B>***.exe!_wassert(const wchar_t * expr=0x04e33520, const wchar_t * filename=0x04e334e0, unsigned int lineno=448) Line 212 C
B>***.exe!`anonymous namespace'::trans_func(unsigned int u=3221225477, _EXCEPTION_POINTERS * ptr=0x062acf4c) Line 448 + 0x18 bytes C++
B>***.exe!_CallSETranslator(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, void * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 701 + 0xd bytes C++
B>***.exe!FindHandlerForForeignException(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, _CONTEXT * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, int curState=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 934 + 0x21 bytes C++
B>***.exe!FindHandler(EHExceptionRecord * pExcept=0x062ad178, EHRegistrationNode * pRN=0x062af59c, _CONTEXT * pContext=0x062ad194, void * pDC=0x062ad14c, const _s_FuncInfo * pFuncInfo=0x05444634, unsigned char recursive=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 866 + 0x25 bytes C++
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Kh_Oleg, Вы писали:
K_O>А также, проверить, чтобы конструктор не вызывал виртуальных методов у ex. K_O>А конструктор, как оказывается, обращается к полю _m_what (напрямую), которое const char* и копирует его себе. Так что тут все будет зависеть от того, куда указывает строка в "выгруженной" exception.
Значит выгружать после копирования, писал, а потом думал
Основная идея в том, что падение происходит внутри диспетчера исключений. Поэтому нужно что бы он спокойно сделал раскрутку, а только потом выгружать dll.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Kh_Oleg, Вы писали:
K_O>Здравствуйте, blackhearted, Вы писали:
B>>Здравствуйте, Kh_Oleg, Вы писали:
K_O>>>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.
B>>Даже если не бросаются исключения? B>>Как же все на плюсах пишут тогда приложения с плагинами? K_O>А ты покажи мне стабильное приложение с плагинами на плюсах. Я таких не знаю. Far — написан на С++ и работает с плагинами, НО: интерфейс к плагинам у него — чистый С.
Суть поста понятна — все стремятся к идеалу,которым является FAR.
Но вопрос был не про стабильность приложения на плюсах, а про проброс исключений из dll и её последующей выгрузкой.
Приложение,в котором это всё происходит — стабильно и этот AV нормально ловится и корректно обрабатывается.
Здравствуйте, Kh_Oleg, Вы писали:
К>>Посмотрел на определение std::exception — оно содержит __declspec(dllimport), во всяком случае, если компилировать с /MD(d). К>>Так что vtable должно жить в msvcrt(d).dll
K_O>__declspec(dllimport) всего лишь делает класс exception экспортируемым из DLL, т.е. видимым в таблице экспорта. Плюс шаманства с декорированием имен. И все.
Не экспортируемым, а импортируемым. А экспортируется этот класс из msvcrt.dll.
В таблице экспорта оказываются и имена функций, и vtable этого класса, тут всё честно должно быть.
Но чтобы убрать сомнения, нужно сделать dumpbin /imports у dll и exe.
Здравствуйте, ononim, Вы писали:
ТКС>>На времени линковки можно сильно сэкономить, в случае разных dll. O>И сильно потерять на времени загрузки приложения.
Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше. Даже если допустить, что оно не возрастет после слияния их в монолитные exe (а оно возрастет, и сильно), это уже вызовет такие потери на простои программистов, что контору сразу можно будет закрывать, штоп не мучалась.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Кодт, Вы писали:
К>Не экспортируемым, а импортируемым. А экспортируется этот класс из msvcrt.dll. К>В таблице экспорта оказываются и имена функций, и vtable этого класса, тут всё честно должно быть.
К>Но чтобы убрать сомнения, нужно сделать dumpbin /imports у dll и exe.
Dump of file Application.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
KERNEL32.dll
41C274 Import Address Table
41C050 Import Name Table
0 time date stamp
0 Index of first forwarder reference
B>Суть поста понятна — все стремятся к идеалу,которым является FAR. B>Но вопрос был не про стабильность приложения на плюсах, а про проброс исключений из dll и её последующей выгрузкой. B>Приложение,в котором это всё происходит — стабильно и этот AV нормально ловится и корректно обрабатывается.