Export (0) Print
Expand All

Managing the Lifetime of an Object

There is a rule for COM interfaces that we have not yet mentioned. Every COM interface must inherit, directly or indirectly, from an interface named IUnknown. This interface provides some baseline capabilities that all COM objects must support.

The IUnknown interface defines three methods:

The QueryInterface method enables a program to query the object's capabilities at run time. We'll say more about that in the next topic, Asking an Object for an Interface. The AddRef and Release methods are used to control the lifetime of an object, which is the subject of this topic.

Reference Counting

Whatever else a program might do, at some point it will allocate and free resources. Allocating a resource is easy. Knowing when to free the resource is hard, especially if the lifetime of the resource extends beyond the current scope. This problem is not unique to COM. Any program that allocates heap memory must solve the same problem. Various solutions have been devised, including automatic destructors (C++) and garbage collection (C#, Java, Lisp). COM uses an approach called reference counting.

Every COM object maintains an internal count, called the reference count. The reference count tracks how many references to the object are currently active. When the number of references drops to zero, the object deletes itself. The last part is worth repeating: The object deletes itself; the program never explicitly deletes the object.

Here are the rules for reference counting:

  • When the object is first created, its reference count is 1. At this point, the program has a single pointer to the object.
  • The program can create a new reference by duplicating (copying) the pointer. Whenever you copy the pointer, you must call the object's AddRef method. This method increments the reference count by one.
  • When you are done using a pointer to the object, you must call Release. The Release method decrements the reference count by one. It also invalidates the pointer. Do not use the pointer again after you call Release. (If you have other pointers to the same object, you can continue to use those pointers.)
  • When you have called Release with every pointer, the object's reference count reaches zero, and the object deletes itself.

The following diagram shows a simple but typical case.

 

Illustration that shows reference counting

Illustration that shows reference counting

 

The program creates an object and stores a pointer (p) to the object. At this point, the reference count is 1. When the program is done using the pointer, it calls Release. The reference count is decremented to zero, and the object deletes itself. Now p is invalid; it is an error to use p for any further method calls.

The next diagram shows a more complicated example.

 

Illustration that shows reference counting

Illustration that shows reference counting

 

Here, the program creates an object and stores the pointer p, as before. Next, the program copies p to a new variable, q. At this point, the program must call AddRef to increment the reference count. The reference count is now 2, and there are two valid pointers to the object. Now suppose the program is done using p. The program calls Release, the reference count goes to 1, and p is no longer valid. However, q is still valid. Later, the program finishes using q, so it calls Release again. The reference count goes to zero, and the object deletes itself.

You might wonder why the program would copy p in the first place. There are two main reasons: First, you might want to store the pointer in a data structure, such as a list. Second, you might want to keep the pointer beyond the current scope of the original variable, so you would copy it to a new variable with wider scope.

One advantage of reference counting is that you can share pointers across different sections of code, without the various code paths coordinating to delete the object. Instead, each code path simply calls Release when that code path is done using the object. The object takes care of deleting itself at the right time.

Example

Here is the code from the Open dialog box example again.


    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | 
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        IFileOpenDialog *pFileOpen;

        hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
                IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

        if (SUCCEEDED(hr))
        {
            hr = pFileOpen->Show(NULL);

            if (SUCCEEDED(hr))
            {
                IShellItem *pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    if (SUCCEEDED(hr))
                    {
                        MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                    pItem->Release();
                }
            }
            pFileOpen->Release();
        }
        CoUninitialize();
    }


Reference counting occurs in two places. First, if program successfully creates the Common Item Dialog object, it must call Release on the pFileOpen pointer.

hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
        IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

if (SUCCEEDED(hr))
{
    // ...
    pFileOpen->Release();
}

Second, when the GetResult method returns a pointer to the IShellItem interface, the program must call Release on the pItem pointer.

hr = pFileOpen->GetResult(&pItem);

if (SUCCEEDED(hr))
{
    // ...
    pItem->Release();
}

Notice that in both cases, the Release call is the last thing that happens before the pointer goes out of scope. Also notice that Release is called only after testing the HRESULT for success. For example, if the call to CoCreateInstance fails, the pFileOpen pointer is not valid, so it would be an error to call Release on the pointer.

Next

Asking an Object for an Interface

 

 

Send comments about this topic to Microsoft

Build date: 10/5/2010

Community Additions

ADD
Show:
© 2015 Microsoft