CoUninitalize will ask a DLL if it is okay to unload now, but the answer is a foregone conclusion

The Dll­Can­Unload­Now entry point is exported by COM in-proc servers. COM host applications call Co­Free­Unused­Libraries periodically to ask COM to do DLL housecleaning, and in response, COM asks each DLL if it is okay to be unloaded. If so, then COM unloads the DLL.

What is not well-known is that COM also does DLL housecleaning when you shut down the last apartment by calling Co­Uninitialize. When that happens, COM will still go around asking each DLL whether it's okay to be unloaded, but the question is merely a formality, because regardless of your answer, COM will unload you anyway.

The story here is that COM is being shut down for the process, so COM knows that when the last Co­Uninitialize is finished, all COM objects will be destroyed. After all, if you don't have COM, then you can't have any COM objects.

As a courtesy, COM will ask you, "Is it okay to unload you?" in case you want to do some early cleanup. But it will ignore your answer.

This means that you need to exercise caution if you call Co­Uninitialize or Co­Free­Unused­Libraries from your COM in-proc server, because the call may end up freeing your code out from under you.

For example, one third-party crash I investigated boiled down to a COM object whose destructor went like this:

 .. blah blah blah ..

 // Let DllCanUnloadNow know that we have one
 // fewer active COM object


It so happened that this was the last COM object created by the DLL, so the _Module.Unlock() call dropped the DLL object count to zero. The COM server then inexplicably called Co­Free­Unused­Libraries (something that is supposed to be called by the host, not a plug-in), and Co­Free­Unused­Libraries did what it was told and asked each DLL, "Hey, do you mind if I unload you now?" The DLL's Dll­Can­Unload­Now function saw that the active COM object count was zero, so it said, "Sure, go ahead."

I hope you see where this is going.

COM unloads your DLL because you said you were okay with it. The call to Co­Free­Unused­Libraries eventually returns, but its return address is inside the My­Com­Object destructor, which was unloaded because you said it was okay to unload.

The fix here is to remove the call to Co­Free­Unused­Libraries. It shouldn't have been there in the first place.

A more common error is creating a background thread without bumping the DLL reference count. When the last COM apartment shuts down, COM will free your DLL, thereby stranding your worker thread. You need to use the Free­Library­And­Exit­Thread trick to keep your DLL loaded until the background thread finishes.

Comments (6)
  1. Niklas says:

    What's with all the COM articles lately? Is it having a renaissance?

    (One time I almost thought it was deprecated in favor of newer technologies.)

    [Huh? My last COM article was in July. -Raymond]
  2. SimonRev says:

    I think he is referring to the fact that you have been using COM in a lot of your little programs lately.

    I don't choose topics based on whether the API happens to be exposed as a COM interface. I didn't realize people thought I did. "Gosh, I should write about topics that happen to use COM interface." -Raymond]
  3. Michael Grier [MSFT] says:

    COM is dead?  Hardly!  COM is the fundamental underpinnings of WinRT the next generation API.  LarryO may cringe with me saying this but basically WinRT == COM + real metadata to describe interfaces instead of typeinfos.  It turns out you don't have to build a whole virtual machine infrastructure to get the benefits of well described metadata.

  4. K says:

    Maybe I’m missing something obvious here but it looks to me that even after removal of the call to CoFreeUnusedLibraries there is still a racing condition between the host calling it and the COM object leaving the destructor.

  5. Crescens2k says:


    According to the MSDN from the documentation for CoFreeUnusedLibrariesEx, CoFreeUnusedLibraries is basically the equivalent of CoFreeUnusedLibrariesEx(INFINITE, 0); and this defaults to a 10 minute delay for MTA and NA threads, if the object is used within that period of time, then Windows will remove it from the pending list and cancel the unload.

    You should read…/ms678413(v=vs.85).aspx for a detailed description.

    The CoFreeUnusedLibraries function is marked as 16 bit compatibility, so it is no wonder that the original design wasn't thread safe.

  6. Niklas says:

    > Huh? My last COM article was in July.

    I kinda connected the article before this one to COM as well. Maybe that was not the case on closer consideration. Well, it was about unloading at least :)

Comments are closed.