Compartilhar via


CComPtr and CComQIPtr, ATL / COM's smart pointers

Until I started working at Microsoft, or maybe even until I started working on Windows, I wasn't much of a "COM in C++" guy.  The functionality that I needed was typically abstracted in to a managed language such as C# and so I could get by by without ever really learning COM.  In fact, I had only written 1 application that used COM (in C++) and I had no idea what I was doing when I created it.  The biggest barrier to learning for me has been the various tasks that most programmers perform once, then reuse, or the various patterns that developers use .  After working on Windows, I have found many of the nuances of COM to be much more trivial than I had first thought.  Every now and then I encounter something new or interesting (in other words, confusing) in code that I am working with.  One of the more interesting patterns has been the CComPtr / CComQIPtr objects.  When working in COM, I typically write code that follows following pattern to get and use objects.

  1. Create the object using CoCreateInstance
  2. Perform my actions with the retrieved object
  3. Release the object...

I have talked about this in previous posts, the process is pretty straightforward and is pretty similar to constructing "normal" objects in C++.

Now, occasionally, this object gets passed around between functions rather than getting accessed globally or using an accessor on the class that contains it.  As such, the object gets passed around as an IUnknown object.  From the unknown interface pointer (pUnknown), I must then get a reference to the original object.  COM's CComPtr and CComQIPtr, both referred to as smart pointers because they keep track of references and deallocate them when the pointer goes out of scope, enable developers to do this.  Each of these objects is also a template class (they can be created for any type of COM object) so they're convenient for converting pointers to unknown objects into usable interfaces based on the template.  The following example shows how an unknown pointer could be translated into a pointer to a COM object for the SomeInterface interface using the CComPtr interface.

CComPtr<ISomInterface> spSomeInterface;
HRESULT hr = pUnk->QueryInterface<ISomeInterface>(&spSomeInterface)
if (SUCCEEDED(hr)){
spSomeInterface->DoSomethingImportant();
}

The pattern is pretty straightforward:

  1. Create the smart pointer
  2. Run QueryInterface to retrieve the interface
  3. Test the HRESULT
  4. Use the queried interface

Alternatively, you can use the CComQIPtr safe pointer and can skip the query interface step as shown in the following example.

CComQIPtr<ISomeInterface> spSomeInterface(pUnk);
if (spSomeInterface){
spSomeInterface->DoSomething();
}

In the case of CComQIPtr, the object will be NULL if it can't be found in the interfaces that the unknown interface has. I won't get into the details of these two interfaces as it seems there are a number of people who prefer one or the other.  More information on this can be found here and here.

Comments

  • Anonymous
    October 11, 2008
    The real evilness of ATL smart pointers (and similar resource managing classes, such as CComBSTR) is their overloading of unary operator& that is not only very non-obvious and counter to the established C++ rules (which, in this case, say simply, "Don't. Ever."), but also break a lot of third-party template libs (including the STL), necessitating those ugly adapters.

  • Anonymous
    October 21, 2008
    The comment has been removed

  • Anonymous
    October 30, 2008
    The problem is that it doesn't do anything smart, unfortunately. Its operator& simply returns a pointer to the raw underlying storage in the pointer. The assumption that you will only ever use it with a non-initialized or reset smart pointer (which is NULL). Looking at the code, it does at least have an assert there. Here's CComPtr<T>::operator& T** operator&() throw() { ATLASSERT(p==NULL); return &p; } Same for strings.