Reglas para implementar QueryInterface

Hay tres reglas principales que rigen la implementación del método IUnknown::QueryInterface en un objeto COM:

  • Los objetos deben tener identidad.
  • El conjunto de interfaces de una instancia de objeto debe ser estático.
  • Debe ser posible consultar correctamente cualquier interfaz en un objeto desde cualquier otra interfaz.

Los objetos deben tener identidad

Para cualquier instancia de objeto determinada, una llamada a QueryInterface con IID_IUnknown siempre debe devolver el mismo valor de puntero físico. Esto le permite llamar a QueryInterface en dos interfaces y comparar los resultados para determinar si apuntan a la misma instancia de un objeto.

El conjunto de interfaces de una instancia de objeto debe ser estático

El conjunto de interfaces accesibles en un objeto a través de QueryInterface debe ser estático, no dinámico. En concreto, si QueryInterface devuelve S_OK para un IID determinado una vez, nunca debe devolver E_NOINTERFACE en las llamadas posteriores en el mismo objeto; y si QueryInterface devuelve E_NOINTERFACE para un IID determinado, las llamadas posteriores para el mismo IID en el mismo objeto nunca deben devolver S_OK.

Debe ser posible consultar correctamente cualquier interfaz en un objeto desde cualquier otra interfaz.

Es decir, dado el código siguiente:

IA * pA = (some function returning an IA *); 
IB * pB = NULL; 
HRESULT   hr; 
hr = pA->QueryInterface(IID_IB, &pB); 
 

se aplican las reglas siguientes:

  • Si tiene un puntero a una interfaz en un objeto, una llamada como la siguiente a QueryInterface para esa misma interfaz debe realizarse correctamente:

    pA->QueryInterface(IID_IA, ...) 
    
    
  • Si una llamada a QueryInterface para un segundo puntero de interfaz se realiza correctamente, una llamada a QueryInterface desde ese puntero para la primera interfaz también debe realizarse correctamente. Si pB se obtuvo correctamente, lo siguiente también debe realizarse correctamente:

    pB->QueryInterface(IID_IA, ...) 
    
    
  • Cualquier interfaz debe ser capaz de consultar cualquier otra interfaz en un objeto. Si pB se obtuvo correctamente y consulta correctamente una tercera interfaz (IC) mediante ese puntero, también debe poder consultar correctamente la IC mediante el primer puntero, pA. En este caso, la siguiente secuencia debe realizarse correctamente:

    IC * pC = NULL; 
    hr = pB->QueryInterface(IID_IC, &pC); 
    pA->QueryInterface(IID_IC, ...) 
    
    

Las implementaciones de interfaz deben mantener un contador de referencias de puntero pendientes a todas las interfaces de un objeto determinado. Debe usar un entero sin signo para el contador.

Si un cliente necesita saber que los recursos se han liberado, debe usar un método en alguna interfaz del objeto con semántica de nivel superior antes de llamar a IUnknown::Release.

Uso e implementación de IUnknown