previous page
next page

Recall: COM Identity

From a client perspective, the rules of AddRef and Release are fairly stringent. Unless the client is careful about their use, objects can go away before expected or can stay around too long. However, the object is allowed to implement AddRef and Release in any number of ways, depending on how it wants to manage its own lifetimefor example, as a heap-based, stack-based, or cached object.

On the other hand, QueryInterface is easy to get right on the client side. Any client can ask an object if it supports any other functionality with a simple call. However, clients expect certain relationships between the interfaces on a COM object. These expectations form the laws of COM identity.

The Laws of COM Identity

The laws of COM identity say the following things about how an object must expose its interfaces via QueryInterface:

  • A client must be capable of getting directly to any interface implemented by the object via QueryInterface.

  • An object's interfaces must be static throughout its lifetime.

  • QueryInterface for IUnknown must always succeed and must always return the same pointer value.

Direct Access to All Interfaces

The COM Specification states that an implementation of QueryInterface must be "reflexive, symmetric, and transitive." This means that, given an interface, a client must be capable of using an interface to get directly to any interface implemented on the object, including the interface the client is using to perform the query. These relationships are mandated to maintain an object's identity in the face of multiple references to the same object. If these relationships are not upheld, a client could find itself with some code that doesn't work just because it asked for the interfaces in the wrong order. With a properly implemented QueryInterface, query order does not matter.

Static Types

Each object can decide for itself whether it wants to expose an interface via QueryInterface, regardless of the class to which it belongs. However, after it has been asked and has answered either "Yes, I support that interface" or "No, I don't support that interface," it must stick to that answer. The reason for this is simple: After an object answers the query, it may never be asked again. For example, a client can pass a resultant interface pointer to another client, which never has to ask the object at all.

The potential for clients "talking among themselves" means that an object cannot use QueryInterface to make client-specific decisionsfor example, those based on security constraints. The object also cannot use QueryInterface to make context decisions that could change during the life of an object, such as time of day. If a client caches an interface pointer returned when the context is favorable, it might not ask again when the context has changed.

An Object's Apartment-Specific Identifier

The remoting layer of COM uses the pointer returned when querying for IUnknown as an object's unique identifier in that apartment. Clients can also compare IUnknown*s as an identity test:

bool AreEqualObjects(IUnknown* punk1, IUnknown* punk2) {
  if( punk1 == null && punk2 == null ) return true;
  if( !punk1 || !punk2 ) return false;
  IUnknown* punka = 0; punk1->QueryInterface(IID_IUnknown,
    (void**)&punka);
  IUnknown* punkb = 0; punk2->QueryInterface(IID_IUnknown,
    (void**)&punkb);
  bool b = (punka == punkb);
  punka->Release(); punkb->Release();
  return b;
}

In fact, the ATL smart pointer classes have a method called IsEqualObject for performing just this comparison:

STDMETHODIMP CBall::SetPlaySurface(IRollSurface* prsNew) {
  if( m_sprs.IsEqualObject(prsNew) ) return S_OK;
  ...
}

However, although COM dictates that the pointer value of IUnknown must always be the same, it places no such restrictions on any other interface. This particular loophole leads to such techniques as tear-off interfaces, discussed further in this chapter.

Nothing Else

As long as these three laws are upheld, an implementation of QueryInterface can be developed using scenes from your most vivid fever dreams. Frankly, I doubt you'll be able to come up with any techniques wackier than those already known, as I present during the rest of this chapter. However, if you do, ATL's implementation of QueryInterface is fully extensible, as you'll see.


previous page
next page
Converted from CHM to HTML with chm2web Pro 2.75 (unicode)