Access violation after catching dll exception

2019-05-12 09:30发布

I have to load modules as dlls dynamically at runtime as they are not known ahead of time, just that they conform to a class interface. What I noticed is that after I catch an exception thrown by the dll (in the main program in the main thread), the right destructors are called and the modules destroyed and dll's unloaded, but then as the } at the end of the catch block is reached by the Visual Studio C++ debugger when stepping line by line, I get another exception which crashes the program with

First-chance exception at 0x68ad2377 (msvcr90d.dll) in xxxxx.exe: 0xC0000005: Access violation reading location 0x02958f14.

If I enable breaking on exceptions, breaking on this second exception shows the location as

msvcr90d.dll!__DestructExceptionObject(EHExceptionRecord * pExcept=0x0017ee4c, unsigned char fThrowNotAllowed=0) Line 1803 + 0xf bytes

but it looks like the frame stack may be corrupt. I can't figure out why this exception is thrown.

A simplified version of my code structure is as follows:

A very simplified structure of the program:

//shared header:
class Module
{
public:
    virtual void Foo(void) = 0;
};


//dll:
class SomeSpecificModule : public Module
{
public:
    virtual void Foo(void);
};

void SomeSpecificModule::Foo(void)
{
    throw 1;
}

extern "C" __declspec(dllexport) Module* GetModule()
{
    return new SomeSpecificModule;
}


//program:
typedef ptrGetModule* (*GetModule)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    ptrGetModule GetModule = (ptrGetModule)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

7条回答
再贱就再见
2楼-- · 2019-05-12 09:50

Are you cathing the exception by value in your actual code? In this case there may be an exception in the destructor of the copied exception object at the end of catch block.

查看更多
疯言疯语
3楼-- · 2019-05-12 09:51

Canopus: when I throw an int as the exception, the same thing happens.

TK___: I am linking to multithreaded dll in all projects.

Assaf and Shing Yip: The dll's are indeed unloaded by FreeLibrary() in the destructor of a wrapper for them, as the wrapper objects I push into a vector of tr1::shared_ptr (as the wrapper itself is noncopyable as a resource holder and so can't be put in an STL vector) existing only in the try{} scope. It seemed like the right thing to do, so I can make sure cleanup including DLL unloading when there is an error situation, and I tend to prefer RAII-style design. If this is the source of the problem, then I'm wondering what sort of design to use that would operate correctly and still look good from a software engineering perspective. What makes me suspect this may not be the problem, however, is that when I step through the destructor calls that occur when the exception is thrown, FreeLibrary() runs with no error, and I can continue stepping until I get to the closing } of the catch{}.

Magnus Skog: In release mode I also get a crash rather than catching the exception then continuing execution normally. Dynamic memory is handled with 1) operator new in a few cases, 2) tr1::shared_ptr, and 3) _mm_malloc/_mm_free where I need alignment.

查看更多
Anthone
4楼-- · 2019-05-12 09:53

I don't see in this code where the DLL is unloaded (as you say it is). Can you please post the relevant code?

The DLL's unloading may be crucial, since your DLL contains the code necessary for destructing objects, unwinding the stack, etc. and it isn't clear from what you posted at which point the DLL is unloaded.

查看更多
我只想做你的唯一
5楼-- · 2019-05-12 09:56

Much of the stack unwinding code that needs to be called when your DLL throws an exception is in the DLL. If you unload the DLL, how is that code to be called?

Don't throw exceptions across dynamically linked module boundaries.

查看更多
欢心
6楼-- · 2019-05-12 09:56

This might be a shot in the dark, but worth checking out.

Your application seems to be compiled in DEBUG since the error shows up in msvcr90d.dll. Are the dlls you are using also compiled in DEBUG? Creating memory with msvcr90.dll and freeing with msvcr90d.dll or vice versa is a common problem when using dlls.

I think your function pointer typedef looks a little suspicious. I'd write it like this:

typedef Module* (*moduleFnType)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    moduleFnType GetModule = (moduleFnType)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

Your typedef doesn't say anything about the return type of the function GetModule.

查看更多
劫难
7楼-- · 2019-05-12 10:00

Check poject settings, if your application is multithreaded then you should link to multithreaded DLL

查看更多
登录 后发表回答