问题
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;
}
回答1:
The thing to remember is that each copy of C runtime library has its own states. If SomeSpecificModule.dll is linking statically to the C runtime library, this kind of problem may happen. If that's the case, try linking with DLL version of of C runtime library. You also have to make sure SomeSpecificModule.dll is compiled and linked exactly the same way as your main module.
You mentioned DLL being unloaded and correct destructors were called, it sounded like your real program has a lot more going on than the sample you posted. If you unloaded SomeSpecificModule.dll in your try block, you've unloaded the exception record for SomeSpecificModule::Foo(), and I guess that's how you got the crash at msvcr90d.dll!__DestructExceptionObject(EHExceptionRecord * ...
However, in general throwing exception across DLL boundaries is asking for trouble. If you're throwing non POD object, you may run into issue with memory allocated by different C runtime library in different heap, different compiler setting, STL version...you get the point.
Change you code so you don't throw across DLL boundaries. One day someone from your team change the compiler setting or a third party header #define changed and your program start crashing you're going to have a very hard time tracing down the root cause.
Anyway, without seeing the real code, I'm just trying to guess what might go wrong. Hope it helps.
回答2:
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.
回答3:
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.
回答4:
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:
Check poject settings, if your application is multithreaded then you should link to multithreaded DLL
回答6:
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.
回答7:
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.
来源:https://stackoverflow.com/questions/967982/access-violation-after-catching-dll-exception