Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Dll unloading issue

Tags:

c++

dll

How can I ensure a dll is not unloaded while any objects in it exist?

The problem is, when I was using explict memory management I could delete the dll objects before freeing the dll, however with smart pointers I have no controll over the order there destroyed, meaning the dll may be freed first causeing a crash when trying to free one of the other objects:

FlPtr is a simple refrence counting class thats calls AddRef and Release as needed

ExampleDll *dll = LoadDll(L"bin\\example.dll");
IObject *obj = dll->CreateObject();
...
obj->Release();
delete dll;//fine because all objects already deleted
return 0;

auto_ptr<ExampleDll> dll = LoadDll(L"bin\\example.dll");
FlPtr<IObject> obj = dll->CreateObject();
...
return 0;//crash if dll is destructed before obj since Object::Release needs to call into the dll

I tried making the dll handle unloading itsself, ie only unload after all objects have been deleted. This work by creating a new object IExampleDll which the dll implements. This is like the ExampleDll object from before but lives in the dll rather than the exe and is also refrence counted. Each object in the dll increments this refrence on contruction and deincrements it on destruction. This means the refrence count only reaches zero when the exe has Released its refrences AND all the dlls objects have been destroyed. It then deletes itsself calling FreeLibrary(GetModuleHandle()) in its destructor.

This however crashes at the FreeLibrary, im asuming because the thread is still in the dlls code that is being unloaded...

I'm at a loss now how to make sure the dll is only unloaded when there are no remaining objects, apart from going back to freeing the dll explicitly after everything else should have been deleted;

int main()
{
    ExampleDll *dll = LoadDll("bin\\example.dll");
    restOfProgram();
    delete dll;
}

This approach becomes difficult when dlls need to be loaded/unloaded mid program saftly, ie if the user changed from d3d to openGL in options.

like image 857
Fire Lancer Avatar asked Jan 20 '09 10:01

Fire Lancer


1 Answers

Assuming you do not want to terminate the thread when unloading the library (otherwise, see MSalters), you need to free the library from the caller that loaded it.

COM solves that by an in-DLL instance counter (much like yours, if I understand you correctly), and regulary checking it by calling a global exported CanUnloadNow function.

Another option is to have your object/interface smart pointers ALSO reference the DLL they came from. This would increase the client data size, but you wouldn't need to touch the DLL. You might even recycle the LoadLibrary/FreeLibrary reference counter, however that might hit performance.

Also, none of these schemes help much if you get circular DLL dependencies (Component DllA.X references DllB.Y, which references DllA.Z). I haven't yet fould a good solution to that that doesn#t requrie global knowledge.

like image 135
peterchen Avatar answered Sep 30 '22 01:09

peterchen