Fun with CoLoadLibrary

This is for the 3 other people in the world still using COM.

We're writing a 'front-end' COM server which wants to delegate work to back-end DLLs. Specifically, it will load up a DLL and pull out an object with the appropriate interface. It sidesteps CoCreateInstance, so we avoid lots of painful registry look-ups when we know exactly which server we want to implement the thing. Why would we do this? Because it allows us to supply our own version of DLL hell, rather than the COM-server-registration one which comes free as standard.

So, is all light and fluffy? Not quite. Normal COM object creation gives you appropriate cross-apartment marshalling and proper DLL unloading. The marshalling isn't a problem - the newly created object just ends up living in the same apartment as the loader object. What about DLL unloading? COM normally allows libraries loaded for the sake of accessing COM servers to be unloaded after a period of unuse through CoFreeUnusedLibraries(Ex). So, if COM didn't load our library, it won't unload it. Argh.

What's specifically the problem? Suppose we have a COM server in the back-end with instances still alive in it. Then it can't be unloaded. Moreover, it can't unload itself when the last instance goes away (FreeLibraryAndExitThread being the nearest thing, but with nasty race conditions). The only thing that knows it needs unloading, and that can unload it, is the front-end library which LoadLibrary'd the back-end. So, this thing must be prevented from unloading until all the back-ends have disappeared.

It looks a mess, since the loaded DLL has to somehow inform the front-end when it's done with, so that the front-end can unload it, and in turn be allowed to unload. Quite a faff. Why can't we just tell COM to unload the back-end for us when all the objects are gone?

Back in the day there was CoLoadLibrary, which had an 'AutoFree' parameter, which did just that. COM would take control of the unloading of the DLL at the appropriate point. Except... it no longer works. MSDN helpfully notes this, after explaining how it would work if it did. In practice, no error is raised if you use this flag. Effectively, it just silently leaks the DLL.

So, how did the COM unloading mechanism work anyway? It calls DllCanUnloadNow on all the COM servers loaded through CoCreateInstance, and on any that return true, it then unloads the DLLs. In the end we just used this mechanism to fake up the COM unloading system. COM would ask our front-end DLL if it could unload, and this would trigger a cascade of DllCanUnloadNows on the back-end DLLs. If they could be unloaded, they got unloaded, and our front-end DLL finally gets unloaded only if it's supporting no objects, and all the libraries it loaded are now unloaded.

It works, but WHY OH WHY COULDN'T THEY JUST LEAVE POOR COLOADLIBRARY ALONE?

Posted 2008-07-23.