views:

168

answers:

4

I recently ran into a problem with a COM object that was using a singleton class factory and had members that were pointers to other COM objects implemented in a different dll than the singleton object. These other COM objects were created by the singleton object and that was the only reference to them. Since the singleton object is never destroyed until its module is unloaded, what seemed to be happening is that sometimes at that point the dll where the other COM objects (members of the singleton) were implemented had already been unloaded and in that case there would be a crash when the singleton released its references to those objects in its destructor. The DLL implementing the other COM objects is aware that there are references held to the objects and returns S_FALSE from its DllCanUnloadNow method. However, this doesn't seem to prevent the DLL from being unloaded always. Is there any safe way to keep member pointers to COM objects from another dll in a singleton COM object?

A: 

If you have a pointer (also known as 'hold a reference') to a COM object, then that COM object shouldn't disappear until you release it.

If a COM object hasn't been released, then the DLL in which its code is implemented shouldn't be unloaded: see DllCanUnloadNow.

ChrisW
Just having a pointer to a COM object doesn't guarantee that the object will be held. You have to call AddRef on the pointer to indicate that you are incrementing the reference count.
casperOne
Jeff's Stong's answer seems better.
ChrisW
"You have to call AddRef" ... I'm not sure: instead, I think that maybe someone else should have already called AddRef before giving you the pointer.
ChrisW
@ChrisW: The point is, SOMEONE has to call AddRef, and it seems that if the singleton is just exposing instance pointers, that they are being consumed without calling AddRef appropriately.
casperOne
If the singleton is exposing a pointer, then the singleton owns the pointer, and AddRef was (presumably) called when the pointer (which the singleton now owns) was created. My money's on Jeff's Stong's answer.
ChrisW
Yeah, I thought DllCanUnloadNow on the other COM objects' DLL should prevent it from being unloaded too. In the debugger though every time it got called it returned S_FALSE and yet sometimes that DLL was still unloaded by the time the destructor of the singleton was invoked.
Will Gorman
+1  A: 

You may very well be suffering from a "wrong" module unload order. See Why are DLLs unloaded in the "wrong" order? for more information.

Jeff Stong
You're right, he might: because run-time loading of COM object via COM factories are the same idea as LoadLibrary ... and the article you cited is about problems when the global destructors are running, which matches the OP's talking about a singleton.
ChrisW
This makes sense because of how the COM dlls are loaded but then I don't understand why DllCanUnloadNow would fail to prevent the other dll from being unloaded.
Will Gorman
If the process is unloading I don't believe that DllCanUnloadNow can prevent the operating system from unloading the DLL.
Jeff Stong
A: 

When the singleton obtains the implementations of COM interfaces, IUnknown::AddRef should be called on the implementation on the pointers when they are obtained.

The same goes for when you access the fields of the singleton which hold these references. When you get the reference, you should call IUnknown::AddRef, and then when done, call IUnknown::Release.

This way, if the singleton is recycled and all the pointers that it holds are released, if you are holding onto one of them in another section of code, you won't lose the instance, since you incremented the reference count.

You should look into using the CComPtr template as well for helping to manage reference counts:

http://msdn.microsoft.com/en-us/library/ezzw7k98(vs.80).aspx

You might also want to look at the rules for reference counting, which you must adhere to:

http://msdn.microsoft.com/en-us/library/ms810016.aspx

Look for the section titled "Reference-Counting Rules"

casperOne
A: 

Assuming Jeff Stong is right, try one of:

  • Don't release the COM object (let it leak).

  • Create a link-time (not just a run-time) dependency from the DLL which contains the singleton to the DLL which contains the COM object.

  • Tell the singleton to die and/or to release the COM object, before the process starts to die and DLLs begin to be forcibly unloaded.

ChrisW