4

I am having an issue where static destructors in a DLL are not getting called. The constructor is being called, but the destructor is not. I have a class like this in my DLL

struct DLLDestructorTest
{
    int blah;
    DLLDestructorTest()
    {
        blah = 2;
    }
    ~DLLDestructorTest()
    {
        blah = 0;
    }
};
DLLDestructorTest DLLDestructorTestObj;

I can put breakpoints in the constructor and the destructor and I can verify that the constructor breakpoint is hit and the destructor breakpoint is not. If I put an analogous chunk of code in my main.cpp file, I can verify that both the constructor and destructor will be called.

The call stack when the constructor is called is as follows.

mydll.dll!mydll::DLLDestructorTest::DLLDestructorTest()  Line 1093  C++
mydll.dll!mydll::`dynamic initializer for 'DLLDestructorTestObj''()  Line 1100 + 0x26 bytes C++
msvcr90d.dll!_initterm(void (void)* * pfbegin=0x000007fee7f521a8, void (void)* * pfend=0x000007fee7f52bf8)  Line 903    C
mydll.dll!_CRT_INIT(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 323 C
mydll.dll!__DllMainCRTStartup(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 540 + 0x13 bytes  C
mydll.dll!_DllMainCRTStartup(void * hDllHandle=0x000007fee7920000, unsigned long dwReason=1, void * lpreserved=0x00000000002cf580)  Line 511    C

Which is basically as described on this webpage http://msdn.microsoft.com/en-us/library/988ye33t.aspx This page give details on how you the constructor is called, but no details as to how the destructor is called. In the following paragraph, it mentions details about which functions are called to invoke the constructor, which I can see in my callstack.

"The C/C++ run-time library code performs the DLL startup sequence, eliminating the need to link with a separate module as was necessary in Windows 3.x. Included in the C/C++ run-time library code is the DLL entry-point function called _DllMainCRTStartup. The _DllMainCRTStartup function does several things, including calling _CRT_INIT, which initializes the C/C++ run-time library and invokes C++ constructors on static, non-local variables. Without this function, the run-time library would be left in an uninitialized state. _CRT_INIT is available for both a statically linked CRT or linking to the CRT DLL Msvcr90.dll, from a user DLL."

I found one post here What happens to global variables declared in a DLL?, but this just seems to state that destructors will be called.

Any ideas as to why the destructors are not called would be appreciated.

Thanks, John Lawrie

Update

I have looked at this and have read up about DLLs but still don't have the solution. The DLL is linked implicitly with __declspec(dllexport). As far as I understand, there is nothing special that I should need to do to unload the DLL. It should unload automatically when the process terminates.

I made an very simple DLL by following the instructions on this page http://msdn.microsoft.com/en-us/library/ms235636(v=vs.90).aspx. I have no problems with the destructors getting called from this project.

I tried comparing what happens between this simple project and my current one. If I put the breakpoint in the simple test project DLL destructor, I can check out the call stack and see that the destructor is called from

crtdll.c line 447
crtdll.c line 560
crtdll.c line 510

In my current project, I can see that it is going into a different __DllMainCRTStartup function and begins executing at line 521. It does eventually get to the same line of line 447 which is just (*function_to_call)(); but my destructor does not get called.

In case it is useful, the DLL I'm calling is JUCE from juce.com

Thanks

Community
  • 1
  • 1
Dalanchoo
  • 177
  • 1
  • 3
  • 17
  • 2
    Sorry if it's a pretty naive comment but...HOW you unload your library? How your program is terminated? If process exits then destructors will be called for sure, if it's terminated then they won't. – Adriano Repetti Mar 19 '14 at 08:42
  • 1
    try printing something using `cout` etc instead of `blah = 0` and see if you get anything printed/logged. – rockoder Mar 19 '14 at 08:52
  • 1
    Using a debugger and setting breakpoints can obscure what is happening. What you should do instead of using the debugger is to call a function such as `OutputDebugString("Destructor is called")`. Then you check to see if that string shows up on your debug monitor/Output window (Visual Studio if you are using it, or if not, something like DebugView from www.sysinternals.com). – PaulMcKenzie Mar 19 '14 at 10:04
  • I have verified using OutputDebugString that the destructor is not called for the destructor in the DLL and is called for the destructor in my main.cpp file. I'm going to now investigate how this DLL is unloaded. As far as I know, the program exits normally, though I am going to investigate this. – Dalanchoo Mar 19 '14 at 17:22
  • So I have examined the flow of my program and this is what I have found. The program appears to exit normally without an exceptions or calls to abort. It returns from WinMain in crtexe.c with a status of 0. Then WinMainCRTStartup calls exit(0). It inside exit that the destructors for my static class in my main.cpp get called. – Dalanchoo Mar 19 '14 at 23:13
  • @Dalanchoo - if you've find the answer, can you answer yourself so the community will learn? – NirMH Jun 01 '14 at 08:45
  • I think you may first verify that your dll is unloaded in the normal manner. You set up a breakpoint in your dll entry point and see whether it was hit when your program exits. If by any reason the dll entry point is not got called, then there will be no way for the destructor to be called. – Tippisum Mar 22 '15 at 08:58

2 Answers2

2

Static destructors are typically registered by the C++ runtime to run via the atexit() function. But they are registered only for applications, not DLLs. And the reason is that DLLs can be loaded and unloaded on the fly. If DLLs registered atexit functions, those function pointers would dangle when the DLL is unloaded and would cause a crash when the program actually exits.

Try to avoid static objects in DLLs. If you absolutely need to have them, then expose initialization and cleanup functions in the library so the user can deal with this. Then convert those variables to pointers and new them on init, delete them on deinit.

Calmarius
  • 18,570
  • 18
  • 110
  • 157
0

This is my experience.

I bound my DLL explicitly by using LoadLibrary. When FreeLibrary called, some access violations has been occurred inside destructors. That unload the DLL immediately and following destructors called from (*function_to_call)(); in the DLL has been cancelled.

A weird thing is Visual Studio doesn't break when the access violation occurred. Then execution has continued from FreeLibrary as if the program done without errors. It looks everything OK except some static objects not having destructor called.

I found bugs with following settings. This changed Visual Studio's behaviour when access violation occurred inside destructors in my DLL.

enter image description here

Ctrl+Alt+E shows this dialog box. At that time I used Visual Studio 2010.

Yorung
  • 64
  • 5