Managing a COM Object's Lifetime

When an object is created, the system allocates the necessary memory resources. When an object is no longer needed, it should be destroyed. The system can use that memory for other purposes. With C++ objects, you can control the object's lifetime directly with the new and delete operators. COM does not enable you to create or destroy objects directly. The reason for this practice is that the same object may be used by more than one application. If one of those applications were to destroy the object, the other applications would probably fail. Instead COM uses a system of reference counting to control an object's lifetime.

An object's reference count is the number of times one of its interfaces has been requested. Each time an interface is requested, the reference count is incremented. An application releases an interface when that interface is no longer needed, decrementing the reference count. As long as the reference count is greater than zero, the object remains in memory. When the reference count reaches zero, the object destroys itself. You don't need to know anything about the reference count of an object. As long as you obtain and release an object's interfaces properly, the object will have the appropriate lifetime.

Properly handling reference counting is a crucial part of COM programming. Failure to do so can easily create a memory leak. One of the most common mistakes that COM programmers make is failing to release an interface. When this happens, the reference count will never reach zero, and the object will remain in memory indefinitely.

Incrementing and Decrementing the Reference Count

Whenever you obtain a new interface pointer, the reference count must be incremented by a call to IUnknown::AddRef. However, your application does not usually need to call this method. If you obtain an interface pointer by calling an object creation method or by calling IUnknown::QueryInterface, the object will automatically increment the reference count. However, if you create an interface pointer in some other way, such as copying an existing pointer, you must explicitly call IUnknown::AddRef. Otherwise, when you release the original interface pointer, the object may be destroyed even though you may still need to use the copy of the pointer.

You must release all interface pointers, regardless of whether you or the object incremented the reference count. When you no longer need an interface pointer, call IUnknown::Release to decrement the reference count. A common practice is to initialize all interface pointers to NULL then set them back to NULL when they are released. That convention allows you to test all interface pointers in your cleanup code. Those that are non-NULL are still active and must be released before you terminate the application.

The following code fragment extends the sample discussed in Using COM Interfaces to illustrate how to handle reference counting.

IDirectSoundBuffer9* pDSBPrimary = NULL;
IDirectSound3DListener9* pDSListener = NULL;
IDirectSound3DListener9* pDSListener2 = NULL;

...

//Create the object and obtain an additional interface.
//The object increments the reference count.

if(FAILED(hr = g_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL )))
  return hr;

if(FAILED(hr=pDSBPrimary->QueryInterface(IID_IDirectSound3DListener9,
                                        (LPVOID *)&pDSListener)))
  return hr;

//Make a copy of the IDirectSound3DListener9 interface pointer.
//Call AddRef to increment the reference count and to ensure that
//the object is not destroyed prematurely

pDSListener2 = pDSListener;
pDSListener2->AddRef();
...
//Cleanup code. Check to see if the pointers are still active.
//If they are, call Release to release the interface.

if(pDSBPrimary != NULL)
{
  pDSBPrimary->Release();
  pDSBPrimary = NULL;
}

if(pDSListener != NULL)
{
  pDSListener->Release();
  pDSListener = NULL;
}

if(pDSListener2 != NULL)
{
  pDSListener2->Release();
  pDSListener2 = NULL;
}