Every COM+ object is guaranteed to support the IUnknown interface, and a pointer to IUnknown can be obtained from CoCreateInstance. Aside from this rule, however, there are no guarantees. QueryInterface determines what other interfaces an object supports. We like to call this the "discovery phase" of the relationship between the client and the object, since the client calls QueryInterface to discover the
capabilities of a particular object. In the following code, the QueryInterface method determines whether an object supports the ISum interface.
If the object supports the desired interface, a pointer to that interface is returned in the second parameter of QueryInterface:
hr = pUnknown->QueryInterface(IID_ISum, (void**)&pSum);
cout << "The IID_ISum interface is not supported. " << endl;
The first parameter to QueryInterface is the IID of the interface being queried for. The IID_ISum value is declared in the component.h file and defined in the component_i.c file. The MIDL compiler generates both of these files based on the ISum interface definition contained in the component.idl file described previously. The component_i.c file contains the actual definitions for the GUIDs defined in the IDL file, as shown below:
The AddRef and Release methods perform reference counting, which is used to determine when an object can be freed from memory. For every interface pointer, you must call AddRef before calling any other methods, and you must call Release after you finish using the interface pointer. From the client's point of view, reference counting takes place on a per-interface basis. To make things more efficient, the objects themselves always call AddRef automatically before QueryInterface returns an interface pointer. For this reason, the client can skip the call to AddRef on interface pointers returned by the QueryInterface method
or from the CoCreateInstance function.
Once we have a pointer to the desired interface, we no longer need the IUnknown pointer that was originally returned by CoCreateInstance. Accordingly, we can call the IUnknown::Release method to decrement the object's reference counter, as shown below. The value returned by the Release method is the interface's reference counter.
Note that calling Release on an interface pointer does not necessarily destroy the object providing the implementation. Release simply decrements the object's reference counter. An object is destroyed when its reference count falls to 0. In the preceding code, we released the IUnknown interface pointer, but the object was not destroyed because the ISum interface pointer returned by QueryInterface caused the
object's reference counter to be incremented via an implicit call to AddRef.
Since we don't have to worry about calling AddRef on the ISum pointer, we are ready to call the Sum
method, as shown in the following code fragment. This, after all, is the goal of the entire sample project.
hr = pSum->Sum(2, 3, &sum);
cout << "Sum(2, 3) = " << sum << endl;
Optimizing reference counting In the example above, we don't need to call the AddRef method on the
pSum interface pointer because AddRef is automatically called on interface pointers returned by the
Declares an HRESULT to use for COM return values.
Declares a pointer to interface IF2.
The client creates an instance of OuterCOMObj asking for a pointer to IF2. The client thinks IF2 is implemented in OuterCOMObj, that is, the aggregated component is not visible to the client. As part of its start-up sequence, OuterCOMObj creates an instance of InnerCOMObj. A pointer to IF2 is returned to the client in pIF2.
Checks to return status of CoCreateInstance.
Assumes we make a series of successful calls into interface IF2.