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.
- Create the object using CoCreateInstance
- Perform my actions with the retrieved object
- 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.
HRESULT hr = pUnk->QueryInterface<ISomeInterface>(&spSomeInterface)
The pattern is pretty straightforward:
- Create the smart pointer
- Run QueryInterface to retrieve the interface
- Test the HRESULT
- 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.
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.