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;
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.