Once you have an initial pointer to an interface on an object, COM has a very simple mechanism to find out whether the object supports another specific interface, and, if so, to get a pointer to it. (For information on getting an initial pointer to an interface on an object, refer to Getting a Pointer to an Object.) This mechanism is the QueryInterface method of the IUnknown interface. If the object supports the requested interface, the method must return a pointer to that interface. This permits an object to navigate freely through the interfaces that an object supports. QueryInterface separates the request "Do you support a given contract?" from the high-performance use of that contract once negotiations have been successful.
When a client initially gains access to an object, that client will receive, at a minimum, an IUnknown interface pointer (the most fundamental interface) through which it can control the lifetime of the object — tell the object when it is done using the object — and invoke QueryInterface. The client is programmed to ask each object it manages to perform some operations, but the IUnknown interface has no functions for those operations. Instead, those operations are expressed through other interfaces. The client is thus programmed to negotiate with objects for those interfaces. Specifically, the client will ask an object — by calling QueryInterface — for an interface through which the client may invoke the desired operations.
Since the object implements QueryInterface, it has the ability to accept or reject the request. If the object accepts the client's request, QueryInterface returns a new pointer to the requested interface to the client. Through that interface pointer, the client has access to the methods of that interface. If, on the other hand, the object rejects the client's request, QueryInterface returns a null pointer — an error — and the client has no pointer through which to call the desired functions. In this case, the client must deal gracefully with that possibility. For example, suppose a client has a pointer to interface A on an object, and asks for interfaces B and C. While the object supports interface B, it does not support interface C. The object returns a pointer to B, and reports that C is not supported.
A key point is that when an object rejects a call to QueryInterface, it is impossible for the client to ask the object to perform the operations expressed through the requested interface. A client must have an interface pointer to invoke methods in that interface. There is no alternative. If the object refuses to provide one, a client must be prepared to do without, either not doing whatever it had intended to do with that object, or attempting to fall back on another, perhaps less powerful, interface. Had the object supported that interface, the client might have done something useful with it. This works well in comparison with other object-oriented systems in which you cannot know whether a function will work until you call that function, and even then, handling failure is uncertain. QueryInterface provides a reliable and consistent way to know whether an object supports an interface before attempting to call its methods.
The QueryInterface method also provides a robust and reliable way for an object to indicate that it does not support a given contract. That is, if in a call to QueryInterface one asks an "old" object whether it supports a "new" interface (one, for example, that was invented after the old object had been shipped), the old object will reliably and, without causing a crash, answer "no." The technology that supports this is the algorithm by which IIDs are allocated. While this may seem like a small point, it is extremely important to the overall architecture of the system, and the ability to inquire of old things about new functionality is, surprisingly, a feature not present in most other object architectures.