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;
}
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.
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.
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.
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.
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:
Your typedef doesn't say anything about the return type of the function GetModule.
Check poject settings, if your application is multithreaded then you should link to multithreaded DLL