When global destructors bite

In my work, I use a lot of ATL.  And in general, I'm pretty impressed with it.  I recently ran into a cool bug that I figured would be worth posting about.

First, what's wrong with the following code?

 

main.cpp:

#include <stdafx.h>

 CComPtr<IUnknown> g_pUnknown;

 

 void __cdecl main(int argc, char *argv[])

 {

    HRESULT hr;

    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    if (hr == S_OK)

    {

           hr = g_pUnknown.CoCreateInstance(CLSID_IXMLDOMDocument);

                   :

                   :

    }

    CoUninitialize();

}

Assume that the program uses ATL and the ATL definitions are included in the stdafx.h file.

Looks pretty simple, right?  Well, if you run the app under the debugger, you're likely to find that it silently access violates when it exits.

The problem occurs because CComPtr's are auto-pointers.  This means that when they’re destroyed, they release the contained pointer.  Normally, that's a good thing - the reference counts are removed and the object is released.  This works AWESOMELY if the CComPtr is scoped to a function or as a member variable in a class.

But when the CComPtr is a global variable, when is the destructor called?

The answer's that the destructor is called when the C runtime library runs down all the global variables.  And that happens when the C runtime library DLL is unloaded.

So why is that a problem?  Well, when the last thread in the process calls CoUninitialize(), COM says "Hey, he's done using COM.  I can now unload all the DLL's that I loaded into the process".  This includes the MSXML3.DLL that contains the XML DOM.  So when the C runtime library runs the destructor for the CComPtr, it tries to release the reference to the embedded IUnknown pointer.  And that tries to access code in the MSXML3.DLL file which is no longer loaded.  And blam!