Problem "Access violation" with virtual destructor for objects created in DLL
Hello,
I'm facing a problem of access violation when deleting an object created in a shared module (DLL). The problem is illustrated in the code below. Basically, the situation is the following. I have a DLL that defines a class hierarchy with virtual destructor. This DLL can load (LoadLibrary) plugins dynamically (plugins are DLL) and the plugin is expected to create some objects of the classes defined in the main DLL. To implement plugin auto-unload, deleting the object created by the plugin is expected to unload (FreeLibrary) the plugin from memory.
Now the problem is that because of the virtual destructor and the fact that the object is created in the plugin, the MSVC compiler generates some "scalar deleting destructor" code that is executed in the plugin context. This means that when deleting the object, the call stack will contain a frame in the plugin address space corresponding to this auto-generated "scalar deleting destructor". However, as the object destructor unload the plugin from memory, this frame becomes invalid (because the plugin code has been unmapped from memory). This leads to an access violation when unwinding the call stack.
Does anybody know what would be the correct way to handle this? Is there a way to avoid MSVC to generate this "scalar deleting destructor" code in the plugin context?
Thanks.
testdll.h#ifndef __testdll_h__
#define __testdll_h__#include <string>
#include <windows.h>#ifdef TESTDLL_BUILD
# define TESTDLL_API __declspec(dllexport)
#else
# define TESTDLL_API __declspec(dllimport)
#endifclass TESTDLL_API A
{
public:
A (void);
virtual ~A (void);
};class TESTDLL_API B : public A
{
public:
B (HMODULE mod);
~B (void);private:
HMODULE mod;
};typedef A* (* A_builder) (HMODULE);
TESTDLL_API A* make_A_from (const std::string& lib);
#endif
testdll.cc#include "testdll.h"
#include <iostream>
#include <windows.h>A::A (void)
{
std::cout << "A::A()" << std::endl;
}A::~A (void)
{
std::cout << "A::~A()" << std::endl;
}B::B (HMODULE hmod)
: mod (hmod)
{
std::cout << "B::B(" << (void*)hmod << ")" << std::endl;
}B::~B (void)
{
std::cout << "B::~B()" << std::endl;
if (mod)
FreeLibrary (mod);
}A* make_A_from (const std::string& lib)
{
HMODULE mod = LoadLibrary (lib.c_str ());
A* retval = 0;if (mod)
{
A_builder ab = reinterpret_cast (GetProcAddress (mod, "make_A"));
if (ab)
retval = ab (mod);
}
return retval;
}testoct.cc#include "testdll.h"
extern "C" __declspec(dllexport) A* make_A (HMODULE mod)
{
B* retval = new B (mod);
return retval;
}testapp.cc#include "testdll.h"
#include <iostream>int main (int argc, char **argv)
{
std::cout << "making A from testoct.oct" << std::endl;A* a = make_A_from ("testoct.oct");
if (a)
{
std::cout << "A object created" << std::endl;
std::cout << "deleting A" << std::endl;
delete a;
std::cout << "A object deleted" << std::endl;
}return 0;
}Makefileall: testdll.dll testoct.oct testapp.exe
testdll.dll: testdll.obj
cl /Zi /LD /Fe$@ testdll.obj user32.lib kernel32.libtestdll.obj: testdll.cc
cl /Zi /EHsc /D_DLL /D_MT /DTESTDLL_BUILD /MD /Fo$@ /c testdll.cctestoct.oct: testoct.obj
cl /Zi /LD /Fe$@ testoct.obj user32.lib kernel32.lib testdll.libtestoct.obj: testoct.cc
cl /Zi /EHsc /D_DLL /D_MT /MD /Fo$@ /c testoct.cctestapp.exe: testapp.obj
cl /Zi /Fe$@ testapp.obj user32.lib kernel32.lib testdll.libtestapp.obj:
testapp.cccl /Zi /EHsc /MD /Fo$@ /c testapp.cc
clean:
del /q *.dll *.oct *.exp *.lib *.exe *.ilk *.obj *.pdb *.manifest *.suo
Answers
All Replies
- Well, the plugin *is* unloading itself in a plugin-kept dtor, so there's no wonder it crashes. Can't you simply unload the dll in whatever loads the plugin in the firstplace?
einaros wrote: Well, the plugin *is* unloading itself in a plugin-kept dtor, so there's no wonder it crashes. Can't you simply unload the dll in whatever loads the plugin in the firstplace? I don't agree that the plugin is unloading itself. All the code (ctor and dtor) for my classes is contained in testdll.dll. What the plugin is doing is simply creating an object of the class B; I don't expect any code of class B to be part of the plugin module. However, MSVC still attaches to my B object some code executed in the plugin context. This means that any object created by a dynamic module, whatever the class (provided it has a virtual dtor) and wherever the implementation code is located, must be deleted before unloading the module. This kind of limitation seems to be MSVC-specific.
- No, you're right. I only glanced briefly, and missed the filenames of the two generated dlls. I'll have a closer look when I get off work.

