There are three main rules than govern implementing the IUnknown::QueryInterface method on a COM object:
Objects must have identity. For any given object instance, a call to QueryInterface must always return the same physical pointer value. This allows you to call QueryInterface(IID_IUnknown, ...) on any two interfaces and compare the results to determine whether they point to the same instance of an object.
The set of interfaces on an object instance must be static. The set of interfaces accessible on an object via QueryInterface must be static, not dynamic. Specifically, if QueryInterface returns S_OK for a given IID once, it must never return E_NOINTERFACE on subsequent calls on the same object, and if QueryInterface returns E_NOINTERFACE for a given IID, subsequent calls for the same IID on the same object must never return S_OK.
It must be possible to query successfully for any interface on an object from any other interface. That is, given the following code:
IA * pA = (some function returning an IA *);
IB * pB = NULL;
HRESULT hr;
hr = pA->QueryInterface(IID_IB, &pB); // line 4
the following rules apply:
pA->QueryInterface(IID_IA, ...)
pB->QueryInterface(IID_IA, ...)
IC * pC = NULL;
hr = pB->QueryInterface(IID_IC, &pC); //Line 7
pA->QueryInterface(IID_IC, ...)
Interface implementations must maintain a counter that is large enough to support 231-1 outstanding pointer references to all the interfaces on a given object. Therefore, you should use a 32-bit unsigned integer for the counter.
If a client needs to know that resources have been freed, it must use a method in some interface on the object with higher-level semantics before calling Release.