This article can be considered the second part for my Client COM article. It's targeted to those who have just starting writing applications which use COM objects. If you have never worked with COM before, please read the Client COM article before going on.
Writing COM client code can be cumbersome. Why, you ask? Because of having to check for the return value of each and every method, querying interfaces for other interfaces, matching up calls to IUnknown::AddRef( ) and IUnknown::QueryInterface( ) (which internally ends up calling AddRef( ) or some similar function) with calls to IUnknown::Release( ) is just not what you want to do manually.
There are numerous classes and libraries designed to solve this very problem. In this article, we'll explore some of the most widely used options proficient COM developers use every day to figure out what tasks can be automated for this kind of COM bookkeeping.
ATL Smart Pointers
ATL is a C++ library shipped with VC++. It's mostly targeted to those writing COM servers, providing numerous services for dealing with COM object implementations. However, there are some smaller parts in this library which fit other purposes as well. Two such smaller parts are ATL Conversion Macros (which I have described in my Unicode article) and ATL Smart Pointers.
Smart pointers are C++ classes which look like and act like pointers, but implement certain facilities which automate some tasks. They are described in nearly every good C++ book, so check out your favorite C++ text to see just how many cool stuff can be done with smart pointers. ATL Smart pointers are capable of automating the following tasks:
- Reference counting COM interfaces
- Managing calls to QueryInterface( )
- Determining if two COM interface pointers belong to the same instance of COM object
- A more elegant way of creating COM objects
- Attach/Detach semantics
- Copying interface pointers
- Providing special services for IDispatch pointers (which are beyond the scope of this article, yet curious readers can consult the CComPtr< IDispatch > specialization)
- Some connection point services (which are beyond the scope of this article, curious readers can consult CComPtr< >::Advise( )
We explore each of these services in detail here.
CComPtr and CComQIPtr are the heart of ATL Smart Pointer support. They are both template classes, with a single parameter which should be the name of the COM interface they should point to. They are both located in namespace ATL. CComQIPtr is derived from CComPtr, and provides services for managing QueryInterface( ) calls. So, any facility discussed for CComPtr applies just as well for CComQIPtr.
Reference counting services
CComPtr pointer takes care when the COM pointer should be Release( )-ed. The destructor of this class checks to see if the internal pointer is non-NULL. If it's non-NULL, then the destructor calls Release( ) on the COM pointer. Typically, you initialize an object of type CComPtr with an interface pointer obtained from somewhere and then forget about releasing it, since the destructor automatically does the right thing.
IMyInterface * GetMyInterface();
void foo()
{
CComPtr< IMyInterface > pInterface( GetMyInterface() );
// use the interface pointer
pInterface->MyMethod();
} // the destructor takes care of releasing the pointer
Managing calls to QueryInterface( )
This support is provided in two levels: inside CComPtr and inside CComQIPtr. CComPtr has a template member function QueryInterface( ) which allows you to QueryInterface( ) a COM interface in a type safe manner. This way, you don't have to worry about passing the necessary IID to QueryInterface( ).
IMyInterface * GetMyInterface();
void foo()
{
CComPtr< IMyInterface > pInterface( GetMyInterface() );
CComPtr< IMyOtherInterface > pOther; // not containing any pointer yet
if (SUCCEEDED(pInterface.QueryInterface( &pOther )))
{
// use pOther
}
} // both pInterface and pOther will be correctly released by the destructors
Note the “&pOther” in the code. As you see, CComPtr acts like a normal C++ pointer, in this manner that you can get its address using the normal & operator. Nifty, isn't it?
But things can be further simplified. CComQIPtr's constructor does the necessary QueryInterface( ) call, so if pOther is a CComQIPtr, you can simply initialize it with pInterface and get the QueryInterface( ) call automagically. Note that you still need to check to make sure the QueryInterface( ) call has succeeded.
IMyInterface * GetMyInterface();
void foo()
{
CComPtr< IMyInterface > pInterface( GetMyInterface() );
CComQIPtr< IMyOtherInterface > pOther( pInterface ); // implicit QI
if (pOther)
{
// use pOther
}
} // both pInterface and pOther will be correctly released by the destructors
You clearly see how much this simplifies the code. In general, there is little reason why you would want to use CComPtr::QueryInterface( ). Always prefer CComQIPtr instead.
One thing is worth mentioning here. CComQIPtr accepts a second template parameter which specifies the IID used in QI call. Most of the times, the compiler detects it on its own using the _uuidof( ) extension (see the MSDN to find out how it works). On rare cases where this does not work, you will get a compiler error, and you will have to pass a pointer to the IID which is usually named in the form of IID__InterfaceName yourself. CComPtr::QueryInterface( ) does not provide this facility.
This article originally appeared on BeginThread.com. It's been republished here, and may contain modifications from the original article.