Question

Let's say I write a DLL in C++, and declare a global object of a class with a non-trivial destructor. Will the destructor be called when the DLL is unloaded?

Was it helpful?

Solution

In a Windows C++ DLL, all global objects (including static members of classes) will be constructed just before the calling of the DllMain with DLL_PROCESS_ATTACH, and they will be destroyed just after the call of the DllMain with DLL_PROCESS_DETACH.

Now, you must consider three problems:

0 - Of course, global non-const objects are evil (but you already know that, so I'll avoid mentionning multithreading, locks, god-objects, etc.)

1 - The order of construction of objects or different compilation units (i.e. CPP files) is not guaranteed, so you can't hope the object A will be constructed before B if the two objects are instanciated in two different CPPs. This is important if B depends on A. The solution is to move all global objects in the same CPP file, as inside the same compilation unit, the order of instanciation of the objects will be the order of construction (and the inverse of the order of destruction)

2 - There are things that are forbidden to do in the DllMain. Those things are probably forbidden, too, in the constructors. So avoid locking something. See Raymond Chen's excellent blog on the subject:

http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx

http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx

In this case, lazy initialization could be interesting: The classes remain in an "un-initialized" state (internal pointers are NULL, booleans are false, whatever) until you call one of their methods, at which point they'll initialize themselves. If you use those objects inside the main (or one of the main's descendant functions), you'll be ok because they will be called after execution of DllMain.

3 - Of course, if some global objects in DLL A depend on global objects in DLL B, you should be very very careful about DLL loading order, and thus dependancies. In this case, DLLs with direct or indirect circular dependancies will cause you an insane amount of headaches. The best solution is to break the circular dependancies.

P.S.: Note that in C++, constructor can throw, and you doesn't want an exception in the middle of a DLL loading, so be sure your global objects won't be using exception without a very very good reason. As correctly written destructors are not authorized to throw, the DLL unloading should be ok in this case.

OTHER TIPS

This page from Microsoft goes into the details of DLL initialization and destruction of globals:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx

If you want to see the actual code that gets executed when linking a .dll, take a look at %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.

From inspection, destructors will be called via _cexit() when the internal reference count maintained by the dll CRT hits zero.

It should be called when either the application ends or the DLL is unloaded, whichever comes first. Note that this is somewhat dependent on the actual runtime you're compiling against.

Also, beware non-trivial destructors as there are both timing and ordering issues. Your DLL may be unloaded after a DLL your destructor relies on, which would obviously cause issues.

When DllMain with fdwReason = DLL_PROCESS_DETACH parameter is called it means the DLL is unloaded by the application. This is the time before the destructor of global/static objects gets called.

In windows binary image files with extension *.exe, *.dll are in PE format Such files have Entry Point. You can view it with dumpbin tool like

dumpbin /headers dllname.dll

If you use C runtime from Microsoft, then your entry point will be something like *CRTStartup or *DllMainCRTStartup

Such functions perform initialization of c and c++ runtime and delegate execution to (main, WinMain) or to DllMain respectively.

If you use Microsofts VC compiler then you can watch at source code of this functions in yours VC directory:

  • crt0.c
  • dllcrt0.c

DllMainCRTStartup process all things need to init/deinit your global variables from .data sections in normal scenario, when it retrive notification DLL_PROCESS_DETACH during dll unload. For example:

  • main or WinMain of startup thread of program returns control flow
  • you explictly call FreeLibrary and use-dll-counter is zero
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top