The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
The latest version of this topic can be found at CComQIPtr Class.
A smart pointer class for managing COM interface pointers.
A COM interface specifying the type of pointer to be stored.
A pointer to the IID of .
ATL uses and CComPtr to manage COM interface pointers, both of which derive from CComPtrBase. Both classes perform automatic reference counting through calls to and Release. Overloaded operators handle pointer operations.
Used to initialize the interface pointer.
A COM interface.
A pointer to the IID of .
The assignment operator.
Used to initialize the interface pointer.
A COM interface.
A pointer to the IID of .
Returns a pointer to the updated object.
The CComPtr and CComQIPtr Smart Pointer Classes
A Review of Smart Pointers
A smart pointer is an object that behaves like a pointer. That is, you can use an instance of a smart pointer class in many of the places you normally use a pointer. However, using a smart pointer provides some advantages over using a raw pointer. For example, a smart interface pointer class can do the following:
Release the encapsulated interface pointer when the class destructor executes.
Automatically release its interface pointer during exception handling when you allocate the smart interface pointer on the stack. This reduces the need to write explicit exception-handling code.
Release the encapsulated interface pointer before overwriting it during an assignment operation.
Call on the interface pointer received during an assignment operation.
Provide different constructors to initialize a new smart pointer through convenient mechanisms.
Be used in many, but not all, the places where you would conventionally use a raw interface pointer.
ATL provides two smart pointer classes: and . The class is a smart COM interface pointer class. You create instances tailored for a specific type of interface pointer. For example, the first line of the following code creates a smart interface pointer. The second line creates a smart custom interface pointer:
The class is a smarter COM interface pointer class that does everything does and more. When you assign to a instance an interface pointer of a different type than the smart pointer, the class calls on the provided interface pointer:
The CComPtr and CComQIPtr Classes
The and classes are similar, with the exception of initialization and assignment. In fact, they're so similar that actually derives from and , in turn, derives from another class, . This latter class defines the actual storage of the underlying raw pointer, and the actual reference-counting operations on that raw pointer. and add constructors and assignment operators. Because of the inheritance relationship between these classes, all the following comments about the class apply equally to the class unless specifically stated otherwise.
The file contains the definition of all three classes. The only state each class maintains is a single public member variable, . This state is defined in the base class:
The first (or, in the case of , only) template parameter specifies the type of the smart interface pointer. The second template parameter to the class specifies the interface ID for the smart pointer. By default, it is the globally unique identifier (GUID) associated with the class of the first parameter. Here are a few examples that use these smart pointer classes. The middle three examples are all equivalent:
Constructors and Destructor
A object can be initialized with an interface pointer of the appropriate type. That is, a object can be initialized using an or another object. Using any other type produces a compiler error. The actual implementation of this behavior is in the class. The default constructor initializes the internal interface pointer to . The other constructors initialize the internal interface pointer to the specified interface pointer. When the specified value is non-, the constructor calls the method. The destructor calls the method on a non- interface pointer.
has a special copy constructor that pulls out the underlying raw interface pointer and passes it to the base class, thus guaranteeing proper and calls.
A object can be initialized with an interface pointer of any type. When the initialization value is the same type as the smart pointer, the constructor simply 's the provided pointer via the base class's constructor:
However, specifying a different type invokes the following constructor, which queries the provided interface pointer for the appropriate interface:
A constructor can never fail. Nevertheless, the call might not succeed. The class sets the internal pointer to when it cannot obtain the required interface. Therefore, you use code such as the following to test whether the object initializes:
You can tell whether the query failed by checking for a pointer, but you cannot determine why it fails. The constructor doesn't save the from a failed call.
The class defines three assignment operators; the class defines three slightly different ones. All the assignment operators do the same actions:
the current interface pointer when it's non-.
the source interface pointer when it's non-.
Save the source interface pointer as the current interface pointer.
The assignment operators are shown here:
The templated version of is interesting. It enables you to assign arbitrary to each other with proper calls made, if necessary. For example, this is now legal:
This begs the question: Why have at all if does the work, too? It appears that the ATL team is moving toward having a single smart interface pointer instead of two and is leaving in place for backward compatibility.
The assignment operators are mostly the same. The only one that does any interesting work is the overload that takes an This queries a non- source interface pointer for the appropriate interface to save. You receive a pointer when the calls fail. As with the equivalent constructor, the for a failed query is not available.
Typically, you use the assignment operator to perform a call. You immediately follow the assignment with a pointer test, as follows:
Object Instantiation Methods
The class provides an overloaded method, called , that you can use to instantiate an object and retrieve an interface pointer on the object. The method has two forms. The first requires the class identifier () of the class to instantiate. The second requires the programmatic identifier ( of the class to instantiate. Both overloaded methods accept optional parameters for the controlling unknown and class context for the instantiation. The controlling unknown parameter defaults to , the normal case, which indicates no aggregation. The class context parameter defaults to , indicating that any available server can service the request.
Notice how the preceding code for the first method creates an instance of the specified class. It passes the parameters of the method to the COM API and, additionally, requests that the initial interface be the interface that the smart pointer class supports. (This is the purpose of the expression.) The second overloaded method translates the provided to a and then creates the instance in the same manner as the first method.
Therefore, the following code is equivalent (although the smart pointer code is easier to read, in my opinion). The first instantiation request explicitly uses the COM API. The second uses the smart pointer method.
CComPtr and CComQIPtr Operations
Because a smart interface pointer should behave as much as possible like a raw interface pointer, the class defines some operators to make the smart pointer objects act like pointers. For example, when you dereference a pointer using , you expect to receive a reference to whatever the pointer points. So dereferencing a smart interface pointer should produce a reference to whatever the underlying interface pointer points to. And it does:
Note that the method kindly asserts (via the macro) when you attempt to dereference a smart interface pointer in a debug build of your component. Of course, I've always considered the General Protection Fault message box to be an equivalent assertion. However, the macro produces a more programmer-friendly indication of the error location.
To maintain the semblance of a pointer, taking the address of a smart pointer objectthat is, invoking should actually return the address of the underlying raw pointer. Note that the issue here isn't the actual binary value returned. A smart pointer contains only the underlying raw interface pointer as its state. Therefore, a smart pointer occupies exactly the same amount of storage as a raw interface pointer. The address of a smart pointer object and the address of its internal member variable are the same binary value.
Without overriding , taking the address of an instance returns a . To have a smart pointer class maintain the same pointer semantics as a pointer of type , the method for the class must return a .
Note that this operator asserts when you take the address of a non- smart interface pointer because you might dereference the returned address and overwrite the internal member variable without properly releasing the interface pointer. It asserts to protect the semantics of the pointer and keep you from accidentally stomping on the pointer. This behavior, however, keeps you from using a smart interface pointer as an function parameter.
When you really want to use a smart pointer in this way, take the address of the member variable:
CComPtr and CComQIPtr Resource-Management Operations
A smart interface pointer represents a resource, albeit one that tries to manage itself properly. Sometimes, though, you want to manage the resource explicitly. For example, you must release all interface pointers before calling the method. This means that you can't wait for the destructor of a object to release the interface pointer when you allocate the object as a global or static variableor even a local variable in . The destructor for global and static variables executes only after the main function exits, long after runs.
You can release the internal interface pointer by assigning to the smart pointer. Alternatively and more explicitly, you can call the method.
Note that the previous code calls the smart pointer object's method because it uses the dot operator to reference the object. It does not directly call the underlying interface pointer's method, as you might expect. The smart pointer's method calls the underlying interface pointer's method and sets the internal interface pointer to . This prevents the destructor from releasing the interface again. Here is the smart pointer's method:
It's not immediately obvious why the method doesn't simply call using its member variable. Instead, it copies the interface pointer member variable into the local variable, sets the member variable to , and then releases the interface using the temporary variable. This approach avoids a situation in which the interface the smart pointer holds is released twice.
For example, assume that the smart pointer is a member variable of class A and that the smart pointer holds a reference to object B. You call the smart pointer's method. The smart pointer releases its reference to object B. Object B, in turn, holds a reference to the class A instance containing the smart pointer. Object B decides to release its reference to the class A instance. The class A instance decides to destruct, which invokes the destructor for the smart pointer member variable. The destructor detects that the interface pointer is non-NULL, so it releases the interface again.
In releases of ATL earlier than version 3, the following code would compile successfully and would release the interface pointer twice. Note the use of the arrow operator.
In those releases of ATL, the arrow operator returned the underlying interface pointer. Therefore, the previous line actually called the function, not the method, as expected. This left the smart pointer's interface pointer member variable non-, so the destructor would eventually release the interface a second time.
This was a nasty bug to find. A smart pointer class encourages you to think about an instance as if it were an interface pointer. However, in this particular case, you shouldn't use the arrow operator (which you would if it actually was a pointer); you had to use the dot operator because it was actually an object. What's worse, the compiler didn't tell you when you got it wrong.
This changed in version 3 of ATL. Note that the current definition of the arrow operator returns a value:
This is a simple template class whose only purpose is to make the and methods inaccessible:
The template class derives from the interface being returned. Therefore, it inherits all the methods of the interface. The class then overrides the and methods, making them private and purely virtual. Now you get the following compiler error when you use the arrow operator to call either of these methods:
The CopyTo Method
The method makes an 'ed copy of the interface pointer and places it in the specified location. Therefore, the method produces an interface pointer that has a lifetime that is separate from the lifetime of the smart pointer that it copies.
Often, you use the method to copy a smart pointer to an parameter. An interface pointer must be 'ed by the code returning the pointer:
Watch out for the following codeit probably doesn't do what you expect, and it isn't correct:
The Type-Cast Operator
When you assign a smart pointer to a raw pointer, you implicitly invoke the method. In other words, you cast the smart pointer to its underlying type. Notice that doesn't the pointer it returns:
That's because you don't want the in the following case:
The Detach and Attach Methods
When you want to transfer ownership of the interface pointer in a instance from the instance to an equivalent raw pointer, use the method. It returns the underlying interface pointer and sets the smart pointer to , ensuring that the destructor doesn't release the interface. The client calling becomes responsible for releasing the interface.
You often use when you need to return to a caller an interface pointer that you no longer need. Instead of providing the caller an 'ed copy of the interface and then immediately releasing your held interface pointer, you can simply transfer the reference to the caller, thus avoiding extraneous / calls. Yes, it's a minor optimization, but it's also simple:
When you want to transfer ownership of a raw interface pointer to a smart pointer, use the method. It releases the interface pointer that the smart pointer holds and then sets the smart pointer to use the raw pointer. Note that, again, this technique avoids extraneous / calls and is a useful minor optimization:
Client code can use the method to assume ownership of a raw interface pointer that it receives as an parameter. The function that provides an parameter is transferring ownership of the interface pointer to the caller.
Miscellaneous Smart Pointer Methods
The smart pointer classes also provide useful shorthand syntax for querying for a new interface: the method. It takes one parameter: the address of a variable that is of the type of the desired interface.
This method reduces the chance of making the common mistake of querying for one interface (for example, ), but specifying a different type of pointer for the returned value (for example, ).
Use the method to determine whether two interface pointers refer to the same object:
This method performs the test for COM identity: Query each interface for and compare the results. A COM object must always return the same pointer value when asked for its interface. The method expands a little on the COM identity test. It considers two interface pointers to be equal objects.
The method associates a site object (specified by the parameter) with the object referenced by the internal pointer. The smart pointer must point to an object that implements the interface.
The method associates a connection point sink object with the object the smart interface pointer references (which is the event source object). The first parameter is the sink interface. You specify the sink interface ID as the second parameter. The third parameter is an output parameter. The method returns a token through this parameter that uniquely identifies this connection.
There is no smart pointer method to end the connection because the pointer is not needed for the . To break the connection, you need only the cookie, the sink interface identifier (IID), and an event source reference.
CComPtr Comparison Operators
Three operators provide comparison operations on a smart pointer. The method returns when the interface pointer is . The method returns when the comparison operand is equal to the interface pointer. The method is rather useless because it compares two interface pointers using their binary values. However, a class needs these comparison operators so that STL collections of class instances work properly.
Using these comparison operators, all the following styles of code work:
The CComPtr Specialization for IDispatch
It's a royal pain to call an object's methods and properties using the method. You have to package all the arguments into structures, build an array of those s, and translate the name of the method to a . It's not only extremely difficult, but it's all tedious and error-prone coding. Here's an example of what it takes to make a simple call to the following method:
Ouch! That's pretty painful for such a simple method call. The code to call a property on an interface is very similar. Fortunately, ATL provides relief from writing code like this.
provides a specialization for dealing with the interface:
Because this class derives from , it inherits the typical smart pointer methods. I examine only the ones that differ significantly from those discussed for the and classes.
Property Accessor and Mutator Methods
A few of the methods make it much easier to get and set properties on an object using the object's interface. First, you can get the for a property, given its string name, by calling the method:
When you have the for a property, you can get and set the property's value using the and methods. You specify the of the property to get or set and send or receive the new value in a structure:
You can skip the initial step and get and set a property given only its name using the well-named and methods:
Method Invocation Helper Functions
The specialization has a number of methods that are customized for the frequent cases of calling an object's method(s) using . Four basic variations exist:
Call a method by or name, passing zero parameters.
Call a method by or name, passing one parameter.
Call a method by or name, passing two parameters.
Call a method by or name, passing an array of N parameters.
Each variation expects the or name of the method to invoke, the arguments, and an optional return value.
Note that when you are creating the parameter arrays, the parameters must be in reverse order: The last parameter should be at element 0, the next-to-last at element 1, and so on.
Using these helper functions, calling the method gets much simpler:
Finally, two static member functions exist: and . You can use these methods to get and set a property using its , even if you haven't encapsulated the pointer in a .
Here's an example:
The CComGITPtr Class
The Global Interface Table (GIT) provides a per-process cache for storing COM interfaces that you can efficiently unmarshal and access from any apartment in a process. COM objects that aggregate the free-threaded marshaler typically use the GIT to unmarshal interfaces that they hold as state because the object never knows which apartment it might be called from. The GIT provides a convenient place where objects that export an interface from their apartment can register interfaces, and where objects that import interfaces into their apartment can unmarshal and use the interface.
Typically, several steps are involved in using the GIT. First, the exporting apartment must use to create an instance of the GIT and obtain an pointer. The exporting apartment then calls to register the interface in the GIT. As a result of the call to , the exporting apartment receives an apartment-neutral cookie that can safely be passed to other apartments (but not other processes) for unmarshaling. Any number of objects in any importing apartment can then use this cookie to retrieve an interface reference that is properly unmarshaled for use in their own apartment.
The code in the exporting apartment might typically look like the following:
The returned to the exporting apartment then is passed to another apartment. By using that cookie, any code in that apartment can retrieve the interface pointer registered in the GIT.
The exporting apartment removes the interface from the GIT by calling and passing in the cookie it originally received.
ATL simplifies the coding required to perform the following steps by encapsulating the GIT functions in the smart pointer class. This class is defined in as follows:
This class accepts an interface type as its template parameter and holds as its only state the cookie for the interface that will be registered in the GIT. The previously described operations that the exporting apartment performed are encapsulated by . Under the covers, simply manipulates the same GIT functions that would otherwise be invoked manually. Even the creation and caching of the GIT itself is managed for you. retrieves a reference to the GIT from , which instantiates the GIT automatically the first time it is accessed and caches the resulting interface pointer for subsequent accesses. This class holds all sorts of information that is global to a COM server; this is discussed in detail in Chapter 5, "COM Servers."
instances can be instantiated with four different constructors:
The first constructor simply initializes the member variable to zero. The second constructor accepts an interface pointer. This constructor retrieves an pointer to a global instance of the GIT and calls . The resulting cookie is cached in . The third constructor accepts a reference to an existing instance of . This overload retrieves the interface associated with the passed-in parameter, reregisters it in the GIT to get a second cookie, and stores the new cookie in its own m_ member variable. This leaves the two s with separate registered copies of the same interface pointer. The fourth constructor accepts a cookie directly and caches the value. One nice thing about this constructor is that, in debug builds, the implementation tries to validate the cookie by retrieving an interface from the GIT using the cookie. The constructor asserts if this fails.
The three assignment operators supplies perform operations identical to those of the corresponding constructors:
What's particularly nice about is that the destructor takes care of the required GIT cleanup when the instance goes out of scope. Beware, thoughthis can get you into a bit of trouble if you're not careful, as you'll learn at the end of this section.
As you can see, the destructor simply delegates its work to the method, which takes care of retrieving an pointer and using it to call .
If you are working with a instance that has already been initialized, you can use one of the methods to associate a different interface with the instance:
The first version of calls if is nonzerothat is, if this is already managing an interface registered in the GIT. It then calls using the new interface passed in and stores the resulting cookie. The second overload also removes the interface it managed from the GIT (if necessary) and then simply caches the cookie provided.
Correspondingly, the method can be used to disassociate the interface from the instance.
This method simply returns the stored cookie value and sets to zero. This means that the caller has now taken ownership of the registered interface pointer and must eventually call .
These methods greatly simplify the code needed to register an interface pointer in the GIT and manage that registration. In fact, the code required in the exporting apartment reduces to a single line.
In the importing apartment, clients that want to use the registered interface pointer simply use the method provides:
This can be used in code like this:
A potentially dangerous race condition occurs if you're not careful using . Remember that the entire reason for having a GIT is to make an interface accessible from multiple threads. This means that you will be passing GIT cookies from an exporting apartment that is not synchronized with code in the importing apartment that will be using the associated registered interface. If the lifetime of the is not carefully managed, the importing apartment could easily end up with an invalid cookie. Here's the scenario:
The trouble with this code is that the method could finish before the retrieves the interface pointer using . This means that the variable will go out of scope and unregister the pointer from the GIT too early. You must employ some manner of synchronization, such as , to guard against problems like these.
In general, shouldn't be used as a local variable. Its intended use is as a member variable or global. In those cases, the lifetime of the object is automatically controlled by the lifetime of the object that contains it, or the lifetime of the process.