Re: Exception from dll -> dll unload -> AV
От: Centaur Россия  
Дата: 27.01.10 11:05
Оценка: -5
Здравствуйте, blackhearted, Вы писали:

B>Что я упустил?


Классы вообще и исключения в частности нельзя использовать через границу DLL.
Re: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 13:25
Оценка: 2 (1)
Здравствуйте, 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
Re[3]: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 14:15
Оценка: 2 (1)
Здравствуйте, blackhearted, Вы писали:

B>
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
Re[9]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 14:13
Оценка: 1 (1)
Здравствуйте, blackhearted, Вы писали:

B>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.


B>Даже если не бросаются исключения?

B>Как же все на плюсах пишут тогда приложения с плагинами?
А ты покажи мне стабильное приложение с плагинами на плюсах. Я таких не знаю. Far — написан на С++ и работает с плагинами, НО: интерфейс к плагинам у него — чистый С.
Re[8]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 27.01.10 14:28
Оценка: 1 (1)
Здравствуйте, ononim, Вы писали:

ТКС>>На времени линковки можно сильно сэкономить, в случае разных dll.

O>И сильно потерять на времени загрузки приложения.

Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше. Даже если допустить, что оно не возрастет после слияния их в монолитные exe (а оно возрастет, и сильно), это уже вызовет такие потери на простои программистов, что контору сразу можно будет закрывать, штоп не мучалась.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[7]: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 14:46
Оценка: 1 (1)
Здравствуйте, Тот кто сидит в пруду, Вы писали:

B>>[q]

B>> try
B>> {
B>>00411851 mov dword ptr [ebp-4],0
lib->>>doAction();
B>>00411858 mov eax,dword ptr [ebp+8]
B>>0041185B mov edx,dword ptr [eax]
B>>0041185D mov esi,esp
B>>0041185F mov ecx,dword ptr [ebp+8]
B>>00411862 mov eax,dword ptr [edx]
B>>00411864 call eax
B>>00411866 cmp esi,esp ; падает тут
B>>00411868 call @ILT+465(__RTC_CheckEsp) (4111D6h)


ТКС>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на 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
Re[10]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 28.01.10 07:29
Оценка: 1 (1)
Здравствуйте, ononim, Вы писали:

ТКС>>>>На времени линковки можно сильно сэкономить, в случае разных dll.

O>>>И сильно потерять на времени загрузки приложения.
ТКС>>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше.
O>
O>вырубить link time code generaion не пробовали на девелоперских конфах?

Хе-хе-хе, издеваешься, да? У нас для некоторых модулей инкрементальную линковку в дебаге отключить пришлось, потому что она работать перестала — слишком большие ilk файлы получаются. C LTCG вообще думаю почти ничего не соберется... А ведь кода-то не так много, меньше миллиона строк. Зато шаблонов и прочих инлайнов полно, как своих, так и библиотечных. Они, судя по map-файлам, основной вклад в размер объектников и дают. Ну а на то, чтобы прошерстить гигабайты объектников и повыкидывать дубликаты, линкеру требуется время и память. Чем больше объектников в одном модуле — тем больше времени и памяти надо.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: Exception from dll -> dll unload -> AV
От: ononim  
Дата: 27.01.10 11:01
Оценка: +1
exception кидать в одном и ловить в другом модуле такая же bad practice как и память выделять в одном и освобождать в другом дефолтовым (а значит неизвестно каким в общем случае) аллокатором
Как много веселых ребят, и все делают велосипед...
Re[2]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 27.01.10 11:17
Оценка: +1
Здравствуйте, Centaur, Вы писали:

B>>Что я упустил?


C>Классы вообще и исключения в частности нельзя использовать через границу DLL.


Бред. Правильно писать "Классы вообще и исключения в частности нельзя использовать через границу DLL, если к тому нет веских причин". Часто такие причины есть, и польза от использования классов и исключений через границы dll многократно перевешивает вред.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 10:48
Оценка:
Что я упустил?
MSVS 2005.

Есть dll.

Library.h

class Library
{
public:
    Library();
    virtual bool doAction();
private:
    std::string someData_;
};


Library.cpp



Library::Library():
    someData_("DATA")
{
    std::cout<<"Library ctor"<<std::endl;
}

bool Library::doAction()
{
    std::cout<<"Library::doAction() ..."<<std::endl;
    std::cout<<"Library::doAction() throw..."<<std::endl;
    throw std::exception("EX REASON");
    return false;
}

//////////////////////////////////////////////////////////////////////////
/// The add-in singleton.
static Library* g_lib = 0;
/// The initialization flag.
static bool g_isInitialised = false;
//////////////////////////////////////////////////////////////////////////
/// Performs one time initialization.
void init()
{
  if(!g_isInitialised)
  {    
    g_isInitialised = true;
  }
}

//////////////////////////////////////////////////////////////////////////
/// The Factory method.
extern "C" __declspec(dllexport) Library* getInstance()
{
  init();

  if(!g_lib)
  {
    g_lib = new Library();
  }

  assert(g_lib);

  return static_cast<Library*>(g_lib);
}

//////////////////////////////////////////////////////////////////////////
/// Release method.
extern "C" __declspec(dllexport) void release()
{
  delete g_lib;
  g_lib = 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
  {
  case DLL_PROCESS_ATTACH:
    break;
  case DLL_THREAD_ATTACH:
    break;
  case DLL_THREAD_DETACH:
    break;
  case DLL_PROCESS_DETACH:
    release();
    break;
  }
  return TRUE;
}


Есть вызывающее ёё приложение



#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


output

Loader .ctor
Library ctor
Dummy .ctor
Dummy::go()
Library::doAction() ...
Library::doAction() throw...
Exception occured...
EX REASON
Loader::unload()
FreeLibrary()...
Rethrowing...

и AV

Unhandled exception at 0x1024c61c (msvcr80d.dll) in Application.exe: 0xC0000005: Access violation reading location 0x10004be0.


Is this expected behavior ?

PS поведение сохраняется если ловить не const std::exception& , а просто std::exception.
И даже если сделать
std::exception e = ex;
std::cout<<"Rethrowing..."<<std::endl;
throw e;
Re[2]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 11:08
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, blackhearted, Вы писали:


B>>Что я упустил?


C>Классы вообще и исключения в частности нельзя использовать через границу DLL.


т.е. нужно редизайнить ?
Какая best practice в таких случаях (обработка исключений брошенных в dll)?
Re[2]: Exception from dll -> dll unload -> AV
От: Кодт Россия  
Дата: 27.01.10 11:59
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Классы вообще и исключения в частности нельзя использовать через границу DLL.


Можно.

В данном случае ошибка, скорее всего, произошла из-за того, что у DLL и EXE разные CRT.
Как следствие,
— нарушение ODR в реализации std::exception (в частности, vtable); попытка вызвать virtual what() и virtual ~exception() из уже несуществующей таблицы
— два менеджера кучи; строка-параметр исключения была создана одним malloc'ом, а освобождается другим

Решение очевидно: компилировать оба проекта с /MD или /MDd.
Перекуём баги на фичи!
Re[3]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 12:33
Оценка:
Здравствуйте, Кодт, Вы писали:

К>В данном случае ошибка, скорее всего, произошла из-за того, что у DLL и EXE разные CRT.

К>Как следствие,
К>- нарушение ODR в реализации std::exception (в частности, vtable); попытка вызвать virtual what() и virtual ~exception() из уже несуществующей таблицы
К>- два менеджера кучи; строка-параметр исключения была создана одним malloc'ом, а освобождается другим

К>Решение очевидно: компилировать оба проекта с /MD или /MDd.


Оба откомпилированы с MDd.
Re[2]: Exception from dll -> dll unload -> AV
От: alsemm Россия  
Дата: 27.01.10 12:41
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, blackhearted, Вы писали:


B>>Что я упустил?


C>Классы вообще и исключения в частности нельзя использовать через границу DLL.

— классы через границы DLL использовать можно и нужно. Не совсем классы конечно, интерфейсы. Пример — COM.
Re: Exception from dll -> dll unload -> AV
От: alsemm Россия  
Дата: 27.01.10 12:54
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>dll бросает исключение.

B>Его ловит Dummy::go() и в catch выгружает dll (в реальном приложении выгрузка происходит в другом потоке ниже по стеку,но результат тот же).
B>Из Dummy::go() делается попытка пробросить исключение дальше и получается AV.
Выгружать dll из которой вылетело исключение перед тем как пробрасывать пойманное исключение дальше — это не очень хорошая идея.
Я бы так переписал:

class exception_wrapper: std::exception
{
    const std::exception& wrapee_;
public:
    explicit exception_wrapper(const std::exception& wrapee): wrapee_(wrapee) {}
    virtual ~exception_wrapper() { loader.unload(); }
};

try
{
    lib->doAction();
}
catch(const std::exception& ex)
{
    std::cout<<"Exception occured..."<<std::endl;
    std::cout<<ex.what()<<std::endl;
    std::cout<<"Rethrowing..."<<std::endl;
    throw exception_wrapper(ex);
}
return false;
Re[4]: Exception from dll -> dll unload -> AV
От: Кодт Россия  
Дата: 27.01.10 12:58
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>Оба откомпилированы с MDd.


Тогда надо под отладчиком запустить и посмотреть в дизассемблере, что именно отстреливается.
Перекуём баги на фичи!
Re: Exception from dll -> dll unload -> AV
От: Fanto  
Дата: 27.01.10 13:11
Оценка:
Здравствуйте, 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;
Re[5]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:15
Оценка:
Здравствуйте, Кодт, Вы писали:


К>Тогда надо под отладчиком запустить и посмотреть в дизассемблере, что именно отстреливается.



try
{
00411851 mov dword ptr [ebp-4],0
lib->doAction();
00411858 mov eax,dword ptr [ebp+8]
0041185B mov edx,dword ptr [eax]
0041185D mov esi,esp
0041185F mov ecx,dword ptr [ebp+8]
00411862 mov eax,dword ptr [edx]
00411864 call eax
00411866 cmp esi,esp ; падает тут
00411868 call @ILT+465(__RTC_CheckEsp) (4111D6h)
}
catch(std::exception ex)
0041186D jmp __catch$?go@Dummy@@QAE_NPAVLibrary@@@Z$0+0C9h (41193Bh)
__catch$?go@Dummy@@QAE_NPAVLibrary@@@Z$0:
00411872 mov byte ptr [ebp-4],2
{
std::cout<<"Exception occured..."<<std::endl;
00411876 mov esi,esp
00411878 mov eax,dword ptr [__imp_std::endl (41C358h)]
0041187D push eax
0041187E push offset string "Exception occured..." (41893Ch)
00411883 mov ecx,dword ptr [__imp_std::cout (41C354h)]
00411889 push ecx
0041188A call std::operator<<<std::char_traits<char> > (411181h)
0041188F add esp,8
00411892 mov ecx,eax
00411894 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (41C350h)]
0041189A cmp esi,esp
0041189C call @ILT+465(__RTC_CheckEsp) (4111D6h)
std::cout<<ex.what()<<std::endl;
004118A1 mov esi,esp
004118A3 mov eax,dword ptr [__imp_std::endl (41C358h)]
004118A8 push eax
004118A9 mov edi,esp
004118AB lea ecx,[ex]
004118AE call dword ptr [__imp_std::exception::what (41C448h)]
004118B4 cmp edi,esp
004118B6 call @ILT+465(__RTC_CheckEsp) (4111D6h)
004118BB push eax
004118BC mov ecx,dword ptr [__imp_std::cout (41C354h)]
004118C2 push ecx
004118C3 call std::operator<<<std::char_traits<char> > (411181h)
004118C8 add esp,8
004118CB mov ecx,eax
004118CD call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (41C350h)]
004118D3 cmp esi,esp
004118D5 call @ILT+465(__RTC_CheckEsp) (4111D6h)
loader.unload();
004118DA mov ecx,offset loader (41B1A0h)
004118DF call Loader::unload (4110C8h)
std::cout<<"Rethrowing..."<<std::endl;
004118E4 mov esi,esp
004118E6 mov eax,dword ptr [__imp_std::endl (41C358h)]
004118EB push eax
004118EC push offset string "Rethrowing..." (41892Ch)
004118F1 mov ecx,dword ptr [__imp_std::cout (41C354h)]
004118F7 push ecx
004118F8 call std::operator<<<std::char_traits<char> > (411181h)
004118FD add esp,8
00411900 mov ecx,eax
00411902 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (41C350h)]
00411908 cmp esi,esp
0041190A call @ILT+465(__RTC_CheckEsp) (4111D6h)
throw;
0041190F push 0
00411911 push 0
00411913 call @ILT+350(__CxxThrowException@8) (411163h)
}


стэк

msvcr80d.dll!FindHandler(EHExceptionRecord * pExcept=0x0012f8bc, EHRegistrationNode * pRN=0x0012ff5c, _CONTEXT * pContext=0x0012f8dc, void * pDC=0x0012f890, 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=0x0012f8bc, EHRegistrationNode * pRN=0x0012ff5c, _CONTEXT * pContext=0x0012f8dc, void * pDC=0x0012f890, 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=0x0012f8bc, void * pContext=0x0012ff5c, void * pDC=0x0012f8dc) 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=0x0012fc5c, 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


Re[2]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:17
Оценка:
Здравствуйте, alsemm, Вы писали:

A>Выгружать dll из которой вылетело исключение перед тем как пробрасывать пойманное исключение дальше — это не очень хорошая идея.


В примере приведен минимальный код
На самом деле всё гораздо сложнее и либа выгружается из другого потока
Re[2]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:19
Оценка:
Здравствуйте, Fanto, Вы писали:

F>А вот так вот пробовали?


F>
F>std::exception e = ex;
F>std::cout<<"Rethrowing..."<<std::endl;
F>loader.unload();
F>throw e;
F>


Конечно
То же самое.
Re[6]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 27.01.10 13:23
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>Здравствуйте, Кодт, Вы писали:



К>>Тогда надо под отладчиком запустить и посмотреть в дизассемблере, что именно отстреливается.



B>[q]

B> try
B> {
B>00411851 mov dword ptr [ebp-4],0
lib->>doAction();
B>00411858 mov eax,dword ptr [ebp+8]
B>0041185B mov edx,dword ptr [eax]
B>0041185D mov esi,esp
B>0041185F mov ecx,dword ptr [ebp+8]
B>00411862 mov eax,dword ptr [edx]
B>00411864 call eax
B>00411866 cmp esi,esp ; падает тут
B>00411868 call @ILT+465(__RTC_CheckEsp) (4111D6h)


Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 13:26
Оценка:
Здравствуйте, 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 возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.
Re[5]: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 13:28
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Тогда надо под отладчиком запустить и посмотреть в дизассемблере, что именно отстреливается.


Боюсь, это придётся делать из 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
Re[5]: Exception from dll -> dll unload -> AV
От: ononim  
Дата: 27.01.10 13:32
Оценка:
K_O>А вообще, корректное использование всех фич С++ через границы DLL возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.
Кроме компилятора еще бывают разные версии STL, BOOST
Кроме того бывают например такие STL, которые замещают стандартный аллокатор своим, который создает свою кучу, в каждом модуле свою...
Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?
Как много веселых ребят, и все делают велосипед...
Re[5]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:39
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем.

K_O>Попробуй не пробрасывать существующее исключение (throw; ), а кинуть его копию (throw ex; )

K_O>А вообще, корректное использование всех фич С++ через границы DLL возможно лишь при соблюдении одного неприятного условия — оба модуля должны быть откомпилированными одним и тем же компилятором. Не разными версиями одного и того же, а строго одним и тем же.


Они откомпилированы строго одним и темже компилятором с разницей в 10 секунд


throw ex;


Те же яйца.
Re[7]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:42
Оценка:
Здравствуйте, Тот кто сидит в пруду, Вы писали:

ТКС>Здравствуйте, blackhearted, Вы писали:


ТКС>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?


Уже порушен — dll выгружена.


_ZwRaiseException@12:
7C90D9AE mov eax,0B5h
7C90D9B3 mov edx,7FFE0300h
7C90D9B8 call dword ptr [edx]
7C90D9BA ret 0Ch


EAX = 000000B5 EBX = 00000000 ECX = 00000206 EDX = 7FFE0300 ESI = 0012F5D4 EDI = 0012F5D4 EIP = 7C90D9B8 ESP = 0012F250 EBP = 0012F534 EFL = 00000206

7FFE0300 = 7C90E510



_KiFastSystemCall@0:
7C90E510 mov edx,esp
7C90E512 sysenter

EAX = 000000B5 EBX = 00000000 ECX = 00000206 EDX = 0012F24C ESI = 0012F5D4 EDI = 0012F5D4 EIP = 7C90E512 ESP = 0012F24C EBP = 0012F534 EFL = 00000206


на sysenter падает

Re[6]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:47
Оценка:
Здравствуйте, ononim, Вы писали:

O>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?


dll к тому,что — это подключаемые модули, которые нет смысла отдавать статически влинкованными всем клиентам.
В качестве замены предлагается статичеки компилировать разные версии для разных клиентов?
Re[6]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 27.01.10 13:52
Оценка:
Здравствуйте, ononim, Вы писали:

O>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?


На времени линковки можно сильно сэкономить, в случае разных dll.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: Exception from dll -> dll unload -> AV
От: Кодт Россия  
Дата: 27.01.10 13:52
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Угу. Позволю сделать предположение, что STL'ные классы не являются частью CRT, поэтому опция MD(d) — что мертвому припарка: таблица виртуальных методов std::exception, порожденной в DLL физически находится в этой (выгруженной) DLL, отсюда и свал при попытке обратиться к виртуальному методу. Правила работы с менеджером памяти (хотя и являются здравым советом) в данном конкретном случае ни при чем.


Посмотрел на определение std::exception — оно содержит __declspec(dllimport), во всяком случае, если компилировать с /MD(d).
Так что vtable должно жить в msvcrt(d).dll
Перекуём баги на фичи!
Re[2]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 13:53
Оценка:
Здравствуйте, 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()

Re[7]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 13:55
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>Здравствуйте, ononim, Вы писали:


O>>Кроме того немного непонятно зачем делать проект состоящий из кучи разных длл, если условием нормальной работы которого является то, чтобы они были скомпилены все вместе одним компилятором и с одним и тем же набором разных либ, то есть фактически — чтоб это былоа единая компиляция. Не проще ли в таком случае сделать монолитный модуль, к чему эти длл?


B>dll к тому,что — это подключаемые модули, которые нет смысла отдавать статически влинкованными всем клиентам.

B>В качестве замены предлагается статичеки компилировать разные версии для разных клиентов?
Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю. Хорошим примером, когда система может выгружать плагины, является Far. Неслучайно поэтому весь Plugin API у него — на С.
Re[3]: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 14:00
Оценка:
Здравствуйте, blackhearted,

Пока посмотрю колстек, но вот идея, мне кажется должно работать:
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_alloc
            return 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
Re[8]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 14:01
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.


Даже если не бросаются исключения?
Как же все на плюсах пишут тогда приложения с плагинами?
Re[8]: Exception from dll -> dll unload -> AV
От: Тот кто сидит в пруду Россия  
Дата: 27.01.10 14:04
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>Здравствуйте, Тот кто сидит в пруду, Вы писали:


ТКС>>Здравствуйте, blackhearted, Вы писали:


ТКС>>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?


B>Уже порушен — dll выгружена.


B>

B>_ZwRaiseException@12:
B>7C90D9AE mov eax,0B5h
B>7C90D9B3 mov edx,7FFE0300h
B>7C90D9B8 call dword ptr [edx]
B>7C90D9BA ret 0Ch


B>

B>EAX = 000000B5 EBX = 00000000 ECX = 00000206 EDX = 7FFE0300 ESI = 0012F5D4 EDI = 0012F5D4 EIP = 7C90D9B8 ESP = 0012F250 EBP = 0012F534 EFL = 00000206

B>7FFE0300 = 7C90E510



B>

B>_KiFastSystemCall@0:
B>7C90E510 mov edx,esp
B>7C90E512 sysenter

B>EAX = 000000B5 EBX = 00000000 ECX = 00000206 EDX = 0012F24C ESI = 0012F5D4 EDI = 0012F5D4 EIP = 7C90E512 ESP = 0012F24C EBP = 0012F534 EFL = 00000206


B>на sysenter падает


А это к чему вообще? Еще один кусочек стека?
И, падает оно явно не на sysenter: "Unhandled exception at 0x1024c61c (msvcr80d.dll)"
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[6]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 14:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 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. Я вот только не пойму, зачем понадобилось выгружать. Очень не всегда это вообще возможно.
Re[4]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 14:12
Оценка:
Здравствуйте, gear nuke, Вы писали:

GN>Здравствуйте, blackhearted,


GN>Пока посмотрю колстек, но вот идея, мне кажется должно работать:

GN>
GN>class Dummy
GN>{
GN>public:

GN>    bool go(Library* lib)
GN>    {
GN>        std::cout<<"Dummy::go()"<<std::endl;
GN>        std::exception const* pex = goimpl(lib);
GN>        if ( pex )
GN>        {
GN>            std::cout<<"Rethrowing..."<<std::endl;
GN>            std::exception ex(*pex);
GN>            delete pex;
GN>            throw ex;
GN>        }
GN>        return false;
GN>    }

GN>    std::exception const* goimpl(Library* lib)
GN>    {
GN>        std::cout<<"Dummy::go()"<<std::endl;
GN>        try
GN>        {
            lib->>doAction();
GN>        }
GN>        catch(const std::exception& ex)
GN>        {
GN>            std::cout<<"Exception occured..."<<std::endl;
GN>            std::cout<<ex.what()<<std::endl;
GN>            loader.unload();
GN>            std::exception const* ce = new std::exception(ex); // но нужно обезопаситься от bad_alloc
GN>            return ce;
GN>        }    
GN>    }
GN>};
GN>

GN> но нужно обезопаситься от bad_alloc
А также, проверить, чтобы конструктор не вызывал виртуальных методов у ex.
А конструктор, как оказывается, обращается к полю _m_what (напрямую), которое const char* и копирует его себе. Так что тут все будет зависеть от того, куда указывает строка в "выгруженной" exception.
Re[7]: Exception from dll -> dll unload -> AV
От: ononim  
Дата: 27.01.10 14:13
Оценка:
ТКС>На времени линковки можно сильно сэкономить, в случае разных dll.
И сильно потерять на времени загрузки приложения.
Как много веселых ребят, и все делают велосипед...
Re[5]: Exception from dll -> dll unload -> AV
От: gear nuke  
Дата: 27.01.10 14:17
Оценка:
Здравствуйте, 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
Re[10]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 14:20
Оценка:
Здравствуйте, Kh_Oleg, Вы писали:

K_O>Здравствуйте, blackhearted, Вы писали:


B>>Здравствуйте, Kh_Oleg, Вы писали:


K_O>>>Далеко не всякий подключаемый модуль позволяет себя выгружать. При использовании С++ через границу DLL возможность выгрузки стремится к нулю.


B>>Даже если не бросаются исключения?

B>>Как же все на плюсах пишут тогда приложения с плагинами?
K_O>А ты покажи мне стабильное приложение с плагинами на плюсах. Я таких не знаю. Far — написан на С++ и работает с плагинами, НО: интерфейс к плагинам у него — чистый С.

Суть поста понятна — все стремятся к идеалу,которым является FAR.
Но вопрос был не про стабильность приложения на плюсах, а про проброс исключений из dll и её последующей выгрузкой.
Приложение,в котором это всё происходит — стабильно и этот AV нормально ловится и корректно обрабатывается.
Re[7]: Exception from dll -> dll unload -> AV
От: Кодт Россия  
Дата: 27.01.10 14:23
Оценка:
Здравствуйте, 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.
Перекуём баги на фичи!
Re[8]: Exception from dll -> dll unload -> AV
От: blackhearted Украина  
Дата: 27.01.10 14:39
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Не экспортируемым, а импортируемым. А экспортируется этот класс из 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

F8 FreeLibrary
1A0 GetProcAddress
171 GetLastError
252 LoadLibraryA
17E GetModuleFileNameW
1A3 GetProcessHeap
210 HeapAlloc
216 HeapFree
1CA GetSystemTimeAsFileTime
143 GetCurrentProcessId
146 GetCurrentThreadId
1DF GetTickCount
2A3 QueryPerformanceCounter
3CC lstrlenA
275 MultiByteToWideChar
394 WideCharToMultiByte
78 DebugBreak
2A7 RaiseException
239 IsDebuggerPresent
34A SetUnhandledExceptionFilter
36E UnhandledExceptionFilter
142 GetCurrentProcess
35E TerminateProcess
226 InterlockedCompareExchange
356 Sleep
229 InterlockedExchange
388 VirtualQuery

MSVCR80D.dll
41C3BC Import Address Table
41C198 Import Name Table
0 time date stamp
0 Index of first forwarder reference

75 _CrtSetCheckCount
1B0 _except_handler4_common
19C _decode_pointer
365 _onexit
2BC _lock
BD __dllonexit
432 _unlock
251 _invoke_watson
178 _controlfp_s
39 ?_type_info_dtor_internal_method@type_info@@QAEXXZ
47 ?terminate@@YAXXZ
187 _crt_debugger_hook
24C _initterm_e
24B _initterm
C7 __initenv
521 exit
163 _cexit
8D _XcptFilter
1B9 _exit
C6 __getmainargs
14C _amsg_exit
10F __set_app_type
1A7 _encode_pointer
F9 __p__fmode
F2 __p__commode
13A _adjust_fdiv
112 __setusermatherr
175 _configthreadlocale
13 ??3@YAXPAX@Z
5F _CRT_RTC_INITW
9 ??0exception@std@@QAE@ABQBDH@Z
7F _CxxThrowException
4A ?what@exception@std@@UBEPBDXZ
A ??0exception@std@@QAE@ABV01@@Z
F ??1exception@std@@UAE@XZ
9A __CxxFrameHandler3
64 _CrtDbgReportW

MSVCP80D.dll
41C320 Import Address Table
41C0FC Import Name Table
0 time date stamp
0 Index of first forwarder reference

952 ?good@ios_base@std@@QBE_NXZ
1A0 ??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z
6D3 ?c_str@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEPBDXZ
2B4 ??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ
65A ?_Unlock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
5D2 ?_Lock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
CD3 ?uncaught_exception@std@@YA_NXZ
5FF ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
853 ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
CAC ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ
8E8 ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
A2B ?length@?$char_traits@D@std@@SAIPBD@Z
CF7 ?width@ios_base@std@@QBEHXZ
8E7 ?flags@ios_base@std@@QBEHXZ
381 ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
72A ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
88E ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDXZ
C30 ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
CF6 ?width@ios_base@std@@QAEHH@Z
C63 ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHPBDH@Z
873 ?eq_int_type@?$char_traits@D@std@@SA_NABH0@Z
859 ?eof@?$char_traits@D@std@@SAHXZ
C60 ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
B6F ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ

Summary

1000 .data
2000 .idata
3000 .rdata
1000 .rsrc
7000 .text
10000 .textbss



Dump of file Library.dll

File Type: DLL

Section contains the following imports:

MSVCR80D.dll
100040CC Import Address Table
10004F08 Import Name Table
0 time date stamp
0 Index of first forwarder reference

1B0 _except_handler4_common
365 _onexit
2BC _lock
BD __dllonexit
432 _unlock
B3 __clean_type_info_names_internal
39 ?_type_info_dtor_internal_method@type_info@@QAEXXZ
47 ?terminate@@YAXXZ
187 _crt_debugger_hook
91 __CppXcptFilter
13A _adjust_fdiv
14C _amsg_exit
24C _initterm_e
24B _initterm
75 _CrtSetCheckCount
19C _decode_pointer
1A8 _encoded_null
1E3 _free_dbg
2CE _malloc_dbg
1A7 _encode_pointer
5F _CRT_RTC_INITW
11 ??2@YAPAXI@Z
473 _wassert
13 ??3@YAXPAX@Z
A ??0exception@std@@QAE@ABV01@@Z
F ??1exception@std@@UAE@XZ
7F _CxxThrowException
B ??0exception@std@@QAE@XZ
9A __CxxFrameHandler3

MSVCP80D.dll
1000406C Import Address Table
10004EA8 Import Name Table
0 time date stamp
0 Index of first forwarder reference

65A ?_Unlock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
5D2 ?_Lock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
2B4 ??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ
381 ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
72A ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
853 ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
1A0 ??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z
C30 ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
CF6 ?width@ios_base@std@@QAEHH@Z
C63 ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHPBDH@Z
873 ?eq_int_type@?$char_traits@D@std@@SA_NABH0@Z
859 ?eof@?$char_traits@D@std@@SAHXZ
C60 ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
B6F ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ
88E ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDXZ
8E7 ?flags@ios_base@std@@QBEHXZ
CF7 ?width@ios_base@std@@QBEHXZ
A2B ?length@?$char_traits@D@std@@SAIPBD@Z
8E8 ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
CAC ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ
952 ?good@ios_base@std@@QBE_NXZ
5FF ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
CD3 ?uncaught_exception@std@@YA_NXZ

KERNEL32.dll
10004000 Import Address Table
10004E3C Import Name Table
0 time date stamp
0 Index of first forwarder reference

F8 FreeLibrary
388 VirtualQuery
17E GetModuleFileNameW
1A3 GetProcessHeap
210 HeapAlloc
216 HeapFree
1CA GetSystemTimeAsFileTime
143 GetCurrentProcessId
146 GetCurrentThreadId
1DF GetTickCount
2A3 QueryPerformanceCounter
252 LoadLibraryA
1A0 GetProcAddress
3CC lstrlenA
275 MultiByteToWideChar
394 WideCharToMultiByte
78 DebugBreak
2A7 RaiseException
239 IsDebuggerPresent
34A SetUnhandledExceptionFilter
36E UnhandledExceptionFilter
142 GetCurrentProcess
35E TerminateProcess
226 InterlockedCompareExchange
356 Sleep
229 InterlockedExchange

Summary

1000 .data
2000 .rdata
1000 .reloc
1000 .rsrc
3000 .text

Re[11]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 27.01.10 14:43
Оценка:
B>Суть поста понятна — все стремятся к идеалу,которым является FAR.
B>Но вопрос был не про стабильность приложения на плюсах, а про проброс исключений из dll и её последующей выгрузкой.
B>Приложение,в котором это всё происходит — стабильно и этот AV нормально ловится и корректно обрабатывается.

Суть прошлого сообщения — не все можно выгружать.
Re[9]: Exception from dll -> dll unload -> AV
От: ononim  
Дата: 27.01.10 23:42
Оценка:
ТКС>>>На времени линковки можно сильно сэкономить, в случае разных dll.
O>>И сильно потерять на времени загрузки приложения.
ТКС>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше.

вырубить link time code generaion не пробовали на девелоперских конфах?
Как много веселых ребят, и все делают велосипед...
Re[2]: Exception from dll -> dll unload -> AV
От: catBasilio  
Дата: 03.02.10 12:28
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, blackhearted, Вы писали:


B>>Что я упустил?


C>Классы вообще и исключения в частности нельзя использовать через границу DLL.


А как же MFC, QT, и другая куча либин?
UNIX way — это когда тебе вместо туалетной бумаги дают топор, рубанок и карту близлежащего леса
Re[3]: Exception from dll -> dll unload -> AV
От: Kh_Oleg  
Дата: 03.02.10 13:28
Оценка:
Здравствуйте, catBasilio, Вы писали:

B>Здравствуйте, Centaur, Вы писали:


C>>Здравствуйте, blackhearted, Вы писали:


B>>>Что я упустил?


C>>Классы вообще и исключения в частности нельзя использовать через границу DLL.


Не так, правильно так: "Классы вообще и исключения в частности нельзя использовать через границу DLL, если модули собраны разными компиляторами"

B>А как же MFC, QT, и другая куча либин?


MFC для каждой версии компилятора идет своя, а "Qt и другая куча либин" — opensource, их всегда можно пересобрать своим компилятором.
Re: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 03.12.11 17:49
Оценка:
так все-таки как поступать, если все компилируется в одном компиляторе, хочется динамическую загрузку библиотек и кидание исключений?

Допустим такой код:

{
   ScopedLoadLibrary lib ( "component.dll" );
   ComponentPtr comp = ComponentFactory::CreateInstance();

   comp->DoSomething(); // и отсюда вылетает некий class myexception : public std::exception
}


Вроде бы нормальный с точки зрения клиентского вызова код.
Но код myexception::~myexception находится в component.dll, а это значит, что в catch-е который его отловит модуль уже будет выгружен.
И когда CRT-шный код пытается удалить объект — происходит AV.

Какие будут предложения?
Re[2]: Exception from dll -> dll unload -> AV
От: Кодт Россия  
Дата: 04.12.11 02:11
Оценка:
Здравствуйте, Amor, Вы писали:

A>Какие будут предложения?


Предложение такое: продлить время жизни библиотеки до самого последнего момента.
Что-то в таком роде
// внутри DLL

HMODULE g_hDLL;
ULONG g_nRef = 0;
void add_ref() { ++g_nRef; }
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
{
  .....
};
// и так везде!

Подобным образом обеспечивается живучесть Inproc COM Server'ов — DLL живёт до тех пор, пока есть COM-объекты, созданные ею.

Подсчёт ссылок удобно делать интрузивным, а то и вообще переложить на плечи ядра (LoadLibrary/FreeLibrary тоже считают ссылки в недрах).
Перекуём баги на фичи!
Re[3]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 04.12.11 05:40
Оценка:
Здравствуйте, Кодт, Вы писали:

К>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, по которой клиентский модуль и принимает решение — выгружать/не выгружать.
Re[2]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 04.12.11 07:57
Оценка:
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 менять нельзя.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 04.12.11 08:00
Оценка:
On 12/04/2011 09:40 AM, Amor wrote:

> Ха. код dec_ref находится в component.dll, вызывает FreeLibrary(себя же), в

> которой выгружается component.dll, return и мы оказываемся в вакууме
> Я пробовал аналогичный вариант, только не через множественное наследование.

Из кода библиотеки нельзя выгружать саму эту библиотеку, моментально получишь
защиту памяти, потому как указатель инструкций процессора после выхода из

FreeLibrary() будет указывать на код этой самой библиотеки, которая уже выгружена.

Нужно поручать выгружать данную библиотеку какому--то другому модулю,
после выхода из кода библиотеки.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 04.12.11 09:18
Оценка:
Здравствуйте, 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-ах.
Re[4]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 05.12.11 08:14
Оценка:
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-и ничего использовать не мешают.
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 05.12.11 09:51
Оценка:
Здравствуйте, 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-и ничего использовать не мешают.

Не мешают конечно, если писать корявый код.
Re[6]: Exception from dll -> dll unload -> AV
От: ndemia Россия http://ndemia.com
Дата: 05.12.11 15:09
Оценка:
Выгружать 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 ничему не мешают. Просто они динамические (а многие про них думают, как про обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая конструкция по сравнению с табуреткой).


A>Не мешают конечно, если писать корявый код.
Re[7]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 05.12.11 16:06
Оценка:
Здравствуйте, 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 (в зависимости от деклараций).



main()
{
   try
   {
      RunProgram();
   }
   catch( std::exception& e )
   {
      std::cout << "Exception occured: " << e.what() << std::endl;
   }
}


Я говорю, что этот код понятия не имеет о том, кто какой компонент там грузит, и далее — какие библиотеки грузит компонент, которого загрузили, и т.д. и т.п.
А ты, так понимаю предлагаешь примерно следующее

main()
{
   ScopedLoadLibrary lib1( "component1.dll" );
   ScopedLoadLibrary lib2( "component2.dll" );
   ScopedLoadLibrary lib3( "component3.dll" );
   try
   {
      RunProgram();
   }
   catch( std::exception& e )
   {
      std::cout << "Exception occured: " << e.what() << std::endl;
   }
}
Re[6]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 05.12.11 16:24
Оценка:
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.


Кто его лочит, или отлочивает -- это без разницы. А он будет
РЕАЛИЗОВЫВАТЬ, обрабатывать запросы на загрузку и выгрузку
компонент.
Posted via RSDN NNTP Server 2.1 beta
Re[8]: Exception from dll -> dll unload -> AV
От: ndemia Россия http://ndemia.com
Дата: 05.12.11 16:26
Оценка:
Здравствуйте, Amor, Вы писали:

A>
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>


Ну, если по простому, то да.
По жизни это может быть кучерявее.
Типа так:


ScopedLoadLibrary* plib = new ScopedLoadLibrary( "component1.dll" );
SomeFunc()
{
   bool f_plib_condemn = false;
   try
   {
      RunProgram();
   }
   catch( std::exception& e )
   {
      std::cout << "Exception occured: " << e.what() << std::endl;
      f_plib_condemn = true;
   }
   if(f_plib_condemn)
   {
      delete plib;
   }
}

потому что это может быть не main (программа может работать дальше), и условие может быть более сложным (например, можно ещё дождаться завершения каких-нибудь тредов и т.п.
Re[7]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 05.12.11 16:29
Оценка:
On 12/05/2011 07:09 PM, ndemia wrote:

> Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что

> это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который
> сработает на конкретной версии конкретного компилятора — всё равно нельзя.
> Потому что в другой версии это не сработает.

Ну, в стандарте таких запретов не будет, просто потому, что там нет .DLL.
Так что спорить можно до посинения и всё равно никому ничего не докажеш.
Тут есть более весомый и очевидный аргумент -- код класса выбрасываемого
объекта находится в этой самой .DLL. Он нужен для копирования объекта
исключения и последующей обработки его в блоке try.

> DLLs ничему не мешают. Просто они динамические (а многие про них думают, как про

> обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая
> конструкция по сравнению с табуреткой).

На самом деле они мало чем отличаются. На высоком уровне.
Posted via RSDN NNTP Server 2.1 beta
Re[8]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 05.12.11 16:31
Оценка:
On 12/05/2011 08:06 PM, Amor wrote:

> Поэтому я и думаю, можно ли найти грамотное решение с точки зрения С++ и ООП,

> чтобы о времени жизни объекта/библиотеки думали только клиенты этого
> объекта/библиотеки.

Понятия "библиотека" нет ни там, ни там.
Ни в С++ , ни в ООП.
Так что как ты их средствами сможеш решить эту проблему -- не понятно.
Posted via RSDN NNTP Server 2.1 beta
Re[9]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 06.12.11 05:01
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Понятия "библиотека" нет ни там, ни там.

MZ>Ни в С++ , ни в ООП.
MZ> Так что как ты их средствами сможеш решить эту проблему -- не понятно.

Ну может попробовать воспринимать "библиотеку" как элемент предметной области.
А вообще если "не понятно", тогда и вправду лучше уйти.
Re[7]: Exception from dll -> dll unload -> AV
От: Amor Россия  
Дата: 06.12.11 05:07
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>On 12/05/2011 01:51 PM, Amor wrote:


>> Код

>>
>> catch( std::exception& e )
>> {
>> }
>>
>>
>> не является клиентом my_exception.

MZ>Как ни пародоксально, является.


Разговор закончен.
Re[8]: Exception from dll -> dll unload -> AV
От: MasterZiv СССР  
Дата: 06.12.11 08:00
Оценка:
On 12/06/2011 09:07 AM, Amor wrote:

> MZ>Как ни пародоксально, является.

>
> Разговор закончен.

Ха! Мы ещё поглядим, кто тут закончит первый !
Posted via RSDN NNTP Server 2.1 beta
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.