#include"../Library/Library.h"#include <iostream>
typedef Library* (*LibFactoryPtr)();
typedef void (*ReleasePtr)();
class Loader
{
public:
Loader():lib_(0)
{
std::cout<<"Loader .ctor"<<std::endl;
LibFactoryPtr getInstanceProc(0);
std::string name("../debug/Library.dll");
hinstLib_ = LoadLibrary(name.c_str());
// If the handle is valid, try to get the function address.if(hinstLib_ == NULL)
throw std::exception("Error loading DLL ", GetLastError());
getInstanceProc = (LibFactoryPtr)GetProcAddress(hinstLib_, "getInstance");
if(getInstanceProc == NULL)
throw std::exception("Error calling function 'getInstance' from DLL ", GetLastError());
lib_ = (getInstanceProc)();
if (lib_ == NULL)
throw std::exception("NULL was returned by function 'getInstance' from DLL", GetLastError());
}
bool unload()
{
std::cout<<"Loader::unload()"<<std::endl;
ReleasePtr releaseProc = (ReleasePtr) GetProcAddress(hinstLib_, "release");
if (releaseProc != NULL)
{
(releaseProc)();
}
std::cout<<"FreeLibrary()..."<<std::endl;
// Free the DLL module.
FreeLibrary(hinstLib_);
return true;
}
Library* get()
{
return lib_;
}
private:
Library* lib_;
HINSTANCE hinstLib_;
};
Loader loader;
class Dummy
{
public:
Dummy()
{
std::cout<<"Dummy .ctor"<<std::endl;
}
bool go(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::cout<<"Rethrowing..."<<std::endl;
throw;
}
return false;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
Library* lib = loader.get();
Dummy dumb;
dumb.go(lib);
}
catch(const std::exception& ex)
{
std::cout<<"Dude...we f*cked up...["<<ex.what()<<"]"<<std::endl;
return 1;
}
return 0;
}
dll бросает исключение.
Его ловит Dummy::go() и в catch выгружает dll (в реальном приложении выгрузка происходит в другом потоке ниже по стеку,но результат тот же).
Из Dummy::go() делается попытка пробросить исключение дальше и получается AV.
call stack
msvcr80d.dll!FindHandler(EHExceptionRecord * pExcept=0x0012f8c4, EHRegistrationNode * pRN=0x0012ff5c, _CONTEXT * pContext=0x0012f8e4, void * pDC=0x0012f898, const _s_FuncInfo * pFuncInfo=0x00419e7c, unsigned char recursive=0, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000) Line 740 + 0x6 bytes C++
msvcr80d.dll!__InternalCxxFrameHandler(EHExceptionRecord * pExcept=0x0012f8c4, EHRegistrationNode * pRN=0x0012ff5c, _CONTEXT * pContext=0x0012f8e4, void * pDC=0x0012f898, const _s_FuncInfo * pFuncInfo=0x00419e7c, int CatchDepth=0, EHRegistrationNode * pMarkerRN=0x00000000, unsigned char recursive=0) Line 524 + 0x25 bytes C++
msvcr80d.dll!__CxxFrameHandler3(EHExceptionRecord * pExcept=0x7c9032a8, EHRegistrationNode * pRN=0x0012f8c4, void * pContext=0x0012ff5c, void * pDC=0x0012f8e4) Line 365 + 0x1f bytes C++
ntdll.dll!_ZwQueryInformationProcess@20() + 0xc bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!ExecuteHandler2@20() + 0x26 bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!_KiUserExceptionDispatcher@8() + 0xe bytes
kernel32.dll!_RaiseException@16() + 0x52 bytes
msvcr80d.dll!_CxxThrowException(void * pExceptionObject=0x0012fc64, const _s__ThrowInfo * pThrowInfo=0x10004bd4) Line 161 C++
10001170()
> Application.exe!Dummy::go(Library * lib=0x00367360) Line 71 + 0xe bytes C++
Application.exe!main(int argc=1, char * * argv=0x003671d0) Line 94 C++
Application.exe!__tmainCRTStartup() Line 597 + 0x19 bytes C
Application.exe!mainCRTStartup() Line 414 C
kernel32.dll!_BaseProcessStart@4() + 0x23 bytes
exception кидать в одном и ловить в другом модуле такая же bad practice как и память выделять в одном и освобождать в другом дефолтовым (а значит неизвестно каким в общем случае) аллокатором
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, blackhearted, Вы писали:
B>>Что я упустил?
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
т.е. нужно редизайнить ?
Какая best practice в таких случаях (обработка исключений брошенных в dll)?
Здравствуйте, Centaur, Вы писали:
B>>Что я упустил?
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
Бред. Правильно писать "Классы вообще и исключения в частности нельзя использовать через границу DLL, если к тому нет веских причин". Часто такие причины есть, и польза от использования классов и исключений через границы dll многократно перевешивает вред.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Centaur, Вы писали:
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
Можно.
В данном случае ошибка, скорее всего, произошла из-за того, что у DLL и EXE разные CRT.
Как следствие,
— нарушение ODR в реализации std::exception (в частности, vtable); попытка вызвать virtual what() и virtual ~exception() из уже несуществующей таблицы
— два менеджера кучи; строка-параметр исключения была создана одним malloc'ом, а освобождается другим
Решение очевидно: компилировать оба проекта с /MD или /MDd.
Здравствуйте, Кодт, Вы писали:
К>В данном случае ошибка, скорее всего, произошла из-за того, что у DLL и EXE разные CRT. К>Как следствие, К>- нарушение ODR в реализации std::exception (в частности, vtable); попытка вызвать virtual what() и virtual ~exception() из уже несуществующей таблицы К>- два менеджера кучи; строка-параметр исключения была создана одним malloc'ом, а освобождается другим
К>Решение очевидно: компилировать оба проекта с /MD или /MDd.
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, blackhearted, Вы писали:
B>>Что я упустил?
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
— классы через границы DLL использовать можно и нужно. Не совсем классы конечно, интерфейсы. Пример — COM.
Здравствуйте, blackhearted, Вы писали:
B>dll бросает исключение. B>Его ловит Dummy::go() и в catch выгружает dll (в реальном приложении выгрузка происходит в другом потоке ниже по стеку,но результат тот же). B>Из Dummy::go() делается попытка пробросить исключение дальше и получается AV.
Выгружать dll из которой вылетело исключение перед тем как пробрасывать пойманное исключение дальше — это не очень хорошая идея.
Я бы так переписал:
Здравствуйте, blackhearted, Вы писали:
B>PS поведение сохраняется если ловить не const std::exception& , а просто std::exception. B>И даже если сделать B>
B>std::exception e = ex;
B>std::cout<<"Rethrowing..."<<std::endl;
B>throw e;
B>
А вот так вот пробовали?
std::exception e = ex;
std::cout<<"Rethrowing..."<<std::endl;
loader.unload();
throw e;
Здравствуйте, alsemm, Вы писали:
A>Выгружать dll из которой вылетело исключение перед тем как пробрасывать пойманное исключение дальше — это не очень хорошая идея.
В примере приведен минимальный код
На самом деле всё гораздо сложнее и либа выгружается из другого потока
Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, blackhearted, Вы писали:
B>dll бросает исключение. B>Его ловит Dummy::go() и в catch выгружает dll (в реальном приложении выгрузка происходит в другом потоке ниже по стеку,но результат тот же).
Можно посмотреть на реальный вариант, включая call-stack?
То, что происходит сейчас, понятно: B>
B>Unhandled exception at 0x1024c61c (msvcr80d.dll) in Application.exe: 0xC0000005: Access violation reading location 0x10004be0.
Падает msvcr80d.dll!FindHandler при попытке распарсить структуру _s__ThrowInfo. Она находится в dll, которая на этот момент выгружена.
Этот контекст был при первом throw std::exception("EX REASON"); — B>
B> msvcr80d.dll!_CxxThrowException(void * pExceptionObject=0x0012fc64, const _s__ThrowInfo * pThrowInfo=0x10004bd4) Line 161 C++
B> 10001170()
throw; перевыбрасывая исключение, восстанавливает первоначальный контекст, но dll уже нет.
Выгружать dll придётся где-то позже Dummy::go(). Другой тред может не дождаться раскрутки при брошенном исключении.
B>PS поведение сохраняется если ловить не const std::exception& , а просто std::exception.
Что бы отделить влияние диспетчера исключений от разных менеджеров куч, можно пробовать бросать например int.
.
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
Здравствуйте, blackhearted, Вы писали:
B>Здравствуйте, Кодт, Вы писали:
К>>В данном случае ошибка, скорее всего, произошла из-за того, что у DLL и EXE разные CRT. К>>Как следствие, К>>- нарушение ODR в реализации std::exception (в частности, vtable); попытка вызвать virtual what() и virtual ~exception() из уже несуществующей таблицы К>>- два менеджера кучи; строка-параметр исключения была создана одним malloc'ом, а освобождается другим
К>>Решение очевидно: компилировать оба проекта с /MD или /MDd.
B>Оба откомпилированы с MDd.
Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем.
Попробуй не пробрасывать существующее исключение (throw; ), а кинуть его копию (throw ex; )
А вообще, корректное использование всех фич С++ через границы DLL возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.
Здравствуйте, Кодт, Вы писали:
К>Тогда надо под отладчиком запустить и посмотреть в дизассемблере, что именно отстреливается.
Боюсь, это придётся делать из WinDbg, MS позаботились что бы отладчик Студии не заходил в диспетчер исключений (для этого там функции _NLG_Notify и т.п.)
.
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
K_O>А вообще, корректное использование всех фич С++ через границы DLL возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.
Кроме компилятора еще бывают разные версии STL, BOOST
Кроме того бывают например такие STL, которые замещают стандартный аллокатор своим, который создает свою кучу, в каждом модуле свою...
Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Kh_Oleg, Вы писали:
K_O>Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем. K_O>Попробуй не пробрасывать существующее исключение (throw; ), а кинуть его копию (throw ex; )
K_O>А вообще, корректное использование всех фич С++ через границы DLL возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.
Они откомпилированы строго одним и темже компилятором с разницей в 10 секунд
Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>Здравствуйте, 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 нормально ловится и корректно обрабатывается.
ТКС>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
Упало действительно "на call eax" но гораздо позже — внутри вызванного кода. Отладчик же посмотрел адрес возврата в стеке и показал cmp. В Студии довольно дубовый отладчик, его разработчики считают что незачем шляться по некоторым местам внутри CRT, отсюда и путанница. Анадогично с sysenter позже — это можно нормально отлаживать через kernel debug WinDbg, запуская отлаживаемый код на другой машине (проще всего виртуальной).
Студией можно пытаться отлаживать исключения, для этого приходится руками ставить точки останова на колбэке ntdll!KiUserExceptionDispatcher и ntdll!RtlUnwind...
.
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
ТКС>>>На времени линковки можно сильно сэкономить, в случае разных dll. O>>И сильно потерять на времени загрузки приложения. ТКС>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше.
вырубить link time code generaion не пробовали на девелоперских конфах?
Как много веселых ребят, и все делают велосипед...
Здравствуйте, ononim, Вы писали:
ТКС>>>>На времени линковки можно сильно сэкономить, в случае разных dll. O>>>И сильно потерять на времени загрузки приложения. ТКС>>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше. O> O>вырубить link time code generaion не пробовали на девелоперских конфах?
Хе-хе-хе, издеваешься, да? У нас для некоторых модулей инкрементальную линковку в дебаге отключить пришлось, потому что она работать перестала — слишком большие ilk файлы получаются. C LTCG вообще думаю почти ничего не соберется... А ведь кода-то не так много, меньше миллиона строк. Зато шаблонов и прочих инлайнов полно, как своих, так и библиотечных. Они, судя по map-файлам, основной вклад в размер объектников и дают. Ну а на то, чтобы прошерстить гигабайты объектников и повыкидывать дубликаты, линкеру требуется время и память. Чем больше объектников в одном модуле — тем больше времени и памяти надо.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, blackhearted, Вы писали:
B>>Что я упустил?
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
А как же MFC, QT, и другая куча либин?
UNIX way — это когда тебе вместо туалетной бумаги дают топор, рубанок и карту близлежащего леса
Здравствуйте, catBasilio, Вы писали:
B>Здравствуйте, Centaur, Вы писали:
C>>Здравствуйте, blackhearted, Вы писали:
B>>>Что я упустил?
C>>Классы вообще и исключения в частности нельзя использовать через границу DLL.
Не так, правильно так: "Классы вообще и исключения в частности нельзя использовать через границу DLL, если модули собраны разными компиляторами"
B>А как же MFC, QT, и другая куча либин?
MFC для каждой версии компилятора идет своя, а "Qt и другая куча либин" — opensource, их всегда можно пересобрать своим компилятором.
так все-таки как поступать, если все компилируется в одном компиляторе, хочется динамическую загрузку библиотек и кидание исключений?
Допустим такой код:
{
ScopedLoadLibrary lib ( "component.dll" );
ComponentPtr comp = ComponentFactory::CreateInstance();
comp->DoSomething(); // и отсюда вылетает некий class myexception : public std::exception
}
Вроде бы нормальный с точки зрения клиентского вызова код.
Но код myexception::~myexception находится в component.dll, а это значит, что в catch-е который его отловит модуль уже будет выгружен.
И когда CRT-шный код пытается удалить объект — происходит AV.
Здравствуйте, Кодт, Вы писали:
К>void dec_ref() { if(--g_nRef) FreeLibrary(g_hDLL); }
К>struct dll_reference К>{ К> dll_reference() { add_ref(); } К> dll_reference(const dll_reference&) { add_ref(); } К> ~dll_reference() { dec_ref(); } К>};
К>struct my_exception : std::exception, dll_reference К>{ К> ..... К>}; К>// и так везде! К>[/c] К>Подобным образом обеспечивается живучесть Inproc COM Server'ов — DLL живёт до тех пор, пока есть COM-объекты, созданные ею.
К>Подсчёт ссылок удобно делать интрузивным, а то и вообще переложить на плечи ядра (LoadLibrary/FreeLibrary тоже считают ссылки в недрах).
Ха. код dec_ref находится в component.dll, вызывает FreeLibrary(себя же), в которой выгружается component.dll, return и мы оказываемся в вакууме
Я пробовал аналогичный вариант, только не через множественное наследование.
template< typename U >
class LockLibrary : public U
{
public:
LockLibrary()
: lib_( ::LoadLibraryA( g_strModuleFileName ) )
{
}
private:
ScopedLoadLibrary lib_;
};
// и в кодеthrow LockLibrary< my_exception >;
все именно так и происходит
В COM-е насколько я знаю есть глобальная функция DllCanUnloadNow, по которой клиентский модуль и принимает решение — выгружать/не выгружать.
On 12/03/2011 09:49 PM, Amor wrote:
> так все-таки как поступать, если все компилируется в одном компиляторе, хочется > динамическую загрузку библиотек и кидание исключений? >
Я не очень понимаю, в чём проблема. В смысле, что тут нет проблемы.
> Допустим такой код: > > { > ScopedLoadLibrary lib ("component.dll" ); > ComponentPtr comp = ComponentFactory::CreateInstance(); > > comp->DoSomething();// и отсюда вылетает некий class myexception : public std::exception > }
> Вроде бы нормальный с точки зрения клиентского вызова код. > Но код myexception::~myexception находится в component.dll, а это значит, что в > catch-е который его отловит модуль уже будет выгружен. > И когда CRT-шный код пытается удалить объект — происходит AV.
как бы если этот exception определён в этом модуле, то клиенты exception-а
(те, кто его использует) будут клиентами и этого модуля. Если модуль
выгружается, когда у него есть действующие (активные) клиентны, это , очевидно,
неправильно, это делать нельзя. Если у тебя такое есть, значит либо .ddl нельзя
выгружать до окончания обработки исключения, либо исключение это надо определять
не в этой .dll, одно из двух.
Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать
отправкой сообщения (PostMessage) о необходимости выгрузки .ddl.
Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль.
Можно ещё (но это вариация на тему выноса исключения) сделать залипуху,
ловить вокруг этого кода исключение, перекодировать его в другое, общее
исключение и перебрасывать. Но это для случаев, когда данную .ddl менять нельзя.
On 12/04/2011 09:40 AM, Amor wrote:
> Ха. код dec_ref находится в component.dll, вызывает FreeLibrary(себя же), в > которой выгружается component.dll, return и мы оказываемся в вакууме > Я пробовал аналогичный вариант, только не через множественное наследование.
Из кода библиотеки нельзя выгружать саму эту библиотеку, моментально получишь
защиту памяти, потому как указатель инструкций процессора после выхода из
FreeLibrary() будет указывать на код этой самой библиотеки, которая уже выгружена.
Нужно поручать выгружать данную библиотеку какому--то другому модулю,
после выхода из кода библиотеки.
Здравствуйте, MasterZiv, Вы писали:
MZ>как бы если этот exception определён в этом модуле, то клиенты exception-а MZ>(те, кто его использует) будут клиентами и этого модуля.
Так в том-то и дело, что приведенный мной код не является клиентом my_exception.
Ему нужен Component, он залочил библиотеку, попользовался компонентом, разлочил.
Точка.
Код catch является клиентом std::exception и ничего об этой DLL не знает.
MZ>Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать MZ>отправкой сообщения (PostMessage) о необходимости выгрузки .ddl.
Тут не понял. Кто кому должен отправить это сообщение?
MZ>Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль.
Я вообщем-то тоже прихожу к такому мнению.
Но это надо бы проверить.
Если я создам my_exception, который экспортируется из другого модуля, то где будет таблица виртуальных функций этого объекта?
MZ>Можно ещё (но это вариация на тему выноса исключения) сделать залипуху, MZ>ловить вокруг этого кода исключение, перекодировать его в другое, общее MZ>исключение и перебрасывать. Но это для случаев, когда данную .ddl менять нельзя.
Уффф... это мне кажется вообще не вариант. Оборачивать каждый вызов?
Легче уж отказаться от использования исключений между dll и делать все на HRESULT-ах.
On 12/04/2011 01:18 PM, Amor wrote:
> MZ>как бы если этот exception определён в этом модуле, то клиенты exception-а > MZ>(те, кто его использует) будут клиентами и этого модуля. > > Так в том-то и дело, что приведенный мной код не является клиентом my_exception. > Ему нужен Component, он залочил библиотеку, попользовался компонентом, разлочил. > Точка. > > Код catch является клиентом std::exception и ничего об этой DLL не знает.
Если он получает через ссылку на std::exception объект-наследник типа
конкретного эксепшена, который реализован в этом компоненте, то этот
код также является клиентом этого конкретного эксепшена и этого компонента.
В С++ так. Может в других языках было бы по-другому.
> > MZ>Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать > MZ>отправкой сообщения (PostMessage) о необходимости выгрузки .ddl. > > Тут не понял. Кто кому должен отправить это сообщение?
Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит
указанный компонент.
> > MZ>Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль. > > Я вообщем-то тоже прихожу к такому мнению. > Но это надо бы проверить. > Если я создам my_exception, который экспортируется из другого модуля, то где > будет таблица виртуальных функций этого объекта?
В модуле, где реализован этот класс, отку да он экспортируется
class __declspec(dllexport) MyClass
{
//...
};
. тебе какая разница ?
> Уффф... это мне кажется вообще не вариант. Оборачивать каждый вызов? > Легче уж отказаться от использования исключений между dll и делать все на > HRESULT-ах.
Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с
невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
Здравствуйте, MasterZiv, Вы писали:
MZ>Если он получает через ссылку на std::exception объект-наследник типа MZ>конкретного эксепшена, который реализован в этом компоненте, то этот MZ>код также является клиентом этого конкретного эксепшена и этого компонента. MZ>В С++ так. Может в других языках было бы по-другому.
Код
catch( std::exception& e )
{
}
не является клиентом my_exception. Это так в любом ООП-языке.
если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
MZ>Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит MZ>указанный компонент.
А кто должен его отправлять?
MZ>В модуле, где реализован этот класс, отку да он экспортируется MZ>. тебе какая разница ?
Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из my_exception.dll и все это время таблица виртуальных функций класса my_exception будет жива.
Только при условии, что my_exception сам лочит component.dll.
MZ>Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с MZ>невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который сработает на конкретной версии конкретного компилятора — всё равно нельзя. Потому что в другой версии это не сработает.
Дело не только во времени жизни вброшенного объекта exception.
Пока мы не вышли из последнего ("неперевбрасывающего") блока catch, обработка throw не завершена. И код DLL в этой обработке участвует (по крайней мере, имеет право участвовать — как вбрасывающая функция).
Здравствуйте, Amor, Вы писали:
A>Здравствуйте, MasterZiv, Вы писали:
MZ>>Если он получает через ссылку на std::exception объект-наследник типа MZ>>конкретного эксепшена, который реализован в этом компоненте, то этот MZ>>код также является клиентом этого конкретного эксепшена и этого компонента. MZ>>В С++ так. Может в других языках было бы по-другому.
A>Код A>
A>catch( std::exception& e )
A>{
A>}
A>
A>не является клиентом my_exception. Это так в любом ООП-языке.
Если my_exception порождён из std::exception, то с чего бы нет? В данном случае "std::exception" — это форма обращения, "внешний вид" того, что по своему происхождению есть "my_exception". И если они прилинкованы из двух разных DLL, то и виртуальные методы одного my_exception могут быть реализованы в разных dll (в зависимости от деклараций).
A>если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
Про ООП думайте как хотите. FreeLibrary — это не ООП. Это "внезапно грохнуть кусок кода, даже если он сейчас выполняется". И вообще загрузка-выгрузка DLL — это не проблема ни C++, ни ООП вообще. Приложение берётся динамически реогранизовывать своё адресное пространство по ходу своей жизни. Естественно, это нужно делать с оглядкой на эту самую жизнь.
MZ>>Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит MZ>>указанный компонент.
A>А кто должен его отправлять?
Его должен отправлять тот код, который принимает на себя ответственность убить DLL, но выдаёт предписание другому коду сделать это в другое время.
Это может быть не обязательно сообщение. Можно, например, в список какой-нибудь занести. Чтобы потом, когда гарантированно закончатся все обработки, связанные с этой DLL, можно было бы её грохнуть.
MZ>>В модуле, где реализован этот класс, отку да он экспортируется MZ>>. тебе какая разница ?
A>Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из my_exception.dll и все это время таблица виртуальных функций класса my_exception будет жива. A>Только при условии, что my_exception сам лочит component.dll.
MZ>>Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с MZ>>невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
DLLs ничему не мешают. Просто они динамические (а многие про них думают, как про обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая конструкция по сравнению с табуреткой).
Здравствуйте, ndemia, Вы писали:
N>Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который сработает на конкретной версии конкретного компилятора — всё равно нельзя. Потому что в другой версии это не сработает.
N>Дело не только во времени жизни вброшенного объекта exception. N>Пока мы не вышли из последнего ("неперевбрасывающего") блока catch, обработка throw не завершена. И код DLL в этой обработке участвует (по крайней мере, имеет право участвовать — как вбрасывающая функция).
Поэтому я и думаю, можно ли найти грамотное решение с точки зрения С++ и ООП, чтобы о времени жизни объекта/библиотеки думали только клиенты этого объекта/библиотеки.
A>>Код A>>
A>>catch( std::exception& e )
A>>{
A>>}
A>>
A>>не является клиентом my_exception. Это так в любом ООП-языке.
N>Если my_exception порождён из std::exception, то с чего бы нет? В данном случае "std::exception" — это форма обращения, "внешний вид" того, что по своему происхождению есть "my_exception". И если они прилинкованы из двух разных DLL, то и виртуальные методы одного my_exception могут быть реализованы в разных dll (в зависимости от деклараций).
Я говорю, что этот код понятия не имеет о том, кто какой компонент там грузит, и далее — какие библиотеки грузит компонент, которого загрузили, и т.д. и т.п.
А ты, так понимаю предлагаешь примерно следующее
On 12/05/2011 01:51 PM, Amor wrote:
> Код > > catch( std::exception& e ) > { > } > > > не является клиентом my_exception.
Как ни пародоксально, является.
Реализация виртуальных методов где ?
Это так в любом ООП-языке.
В некоторых -- да. Но не в С++.
> если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
Ха. Это как-то наивно выглядит. С чего я это уходить куда-то должен?
> А кто должен его отправлять?
Код, который хочет выгрузить .DLL и находится в этой .DLL.
В общем случае лучше -- любой код, который откуда-то хочет выгрузить .DLL
> Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из > my_exception.dll и все это время таблица виртуальных функций класса my_exception > будет жива.
А, дошло таки.
> Только при условии, что my_exception сам лочит component.dll.
Кто его лочит, или отлочивает -- это без разницы. А он будет
РЕАЛИЗОВЫВАТЬ, обрабатывать запросы на загрузку и выгрузку
компонент.
A>main()
A>{
A> try
A> {
A> RunProgram();
A> }
A> catch( std::exception& e )
A> {
A> std::cout << "Exception occured: " << e.what() << std::endl;
A> }
A>}
A>
A>Я говорю, что этот код понятия не имеет о том, кто какой компонент там грузит, и далее — какие библиотеки грузит компонент, которого загрузили, и т.д. и т.п.
Этот — не имеет. Другой, может, будет иметь. Какая разница?
Например, есть вызов e.what(). Из этого кода не следует, что это не my_exception::what(). Т.е. данный код имеет реальный шанс вызвать код my_exception, не имея об этом никакого понятия. С точки зрения ООП/Си++ никто его ругать за это права не имеет. А вот с точки зрения организации процесса, если мы перед этим выгрузим код функции my_exception::what — всё накроется. Хотя объект my_exception будет ещё жив, и его VTBL в порядке.
A>А ты, так понимаю предлагаешь примерно следующее
A>
A>main()
A>{
A> ScopedLoadLibrary lib1( "component1.dll" );
A> ScopedLoadLibrary lib2( "component2.dll" );
A> ScopedLoadLibrary lib3( "component3.dll" );
A> try
A> {
A> RunProgram();
A> }
A> catch( std::exception& e )
A> {
A> std::cout << "Exception occured: " << e.what() << std::endl;
A> }
A>}
A>
Ну, если по простому, то да.
По жизни это может быть кучерявее.
Типа так:
потому что это может быть не main (программа может работать дальше), и условие может быть более сложным (например, можно ещё дождаться завершения каких-нибудь тредов и т.п.
On 12/05/2011 07:09 PM, ndemia wrote:
> Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что > это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который > сработает на конкретной версии конкретного компилятора — всё равно нельзя. > Потому что в другой версии это не сработает.
Ну, в стандарте таких запретов не будет, просто потому, что там нет .DLL.
Так что спорить можно до посинения и всё равно никому ничего не докажеш.
Тут есть более весомый и очевидный аргумент -- код класса выбрасываемого
объекта находится в этой самой .DLL. Он нужен для копирования объекта
исключения и последующей обработки его в блоке try.
> DLLs ничему не мешают. Просто они динамические (а многие про них думают, как про > обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая > конструкция по сравнению с табуреткой).
На самом деле они мало чем отличаются. На высоком уровне.
On 12/05/2011 08:06 PM, Amor wrote:
> Поэтому я и думаю, можно ли найти грамотное решение с точки зрения С++ и ООП, > чтобы о времени жизни объекта/библиотеки думали только клиенты этого > объекта/библиотеки.
Понятия "библиотека" нет ни там, ни там.
Ни в С++ , ни в ООП.
Так что как ты их средствами сможеш решить эту проблему -- не понятно.
Здравствуйте, MasterZiv, Вы писали:
MZ>Понятия "библиотека" нет ни там, ни там. MZ>Ни в С++ , ни в ООП. MZ> Так что как ты их средствами сможеш решить эту проблему -- не понятно.
Ну может попробовать воспринимать "библиотеку" как элемент предметной области.
А вообще если "не понятно", тогда и вправду лучше уйти.