Compartir a través de


Uso de CUnknown

[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.

DirectShow implementa IUnknown en una clase base denominada CUnknown. Puede usar CUnknown para derivar otras clases, reemplazando solo los métodos que cambian entre componentes. La mayoría de las demás clases base de DirectShow derivan de CUnknown, por lo que el componente puede heredar directamente de CUnknown o de otra clase base.

INonDelegatingUnknown

CUnknown implementa INonDelegatingUnknown. Administra internamente los recuentos de referencias y, en la mayoría de las situaciones, la clase derivada puede heredar los dos métodos de recuento de referencias sin ningún cambio. Tenga en cuenta que CUnknown se elimina cuando el recuento de referencias cae en cero. Por otro lado, debe invalidar CUnknown::NonDelegatingQueryInterface, ya que el método de la clase base devuelve E_NOINTERFACE si recibe cualquier IID distinto de IID_IUnknown. En la clase derivada, pruebe los IID de las interfaces que admite, como se muestra en el ejemplo siguiente:

STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
    if (riid == IID_ISomeInterface)
    {
        return GetInterface((ISomeInterface*)this, ppv);
    }
    // Default: Call parent class method. 
    // The CUnknown class must be in the inheritance chain.
    return CParentClass::NonDelegatingQueryInterface(riid, ppv);
}

La función de utilidad GetInterface (vea Funciones auxiliares COM) establece el puntero, incrementa el recuento de referencias de forma segura para subprocesos y devuelve S_OK. En el caso predeterminado, llame al método de clase base y devuelva el resultado. Si deriva de otra clase base, llame a su método NonDelegatingQueryInterface en su lugar. Esto le permite admitir todas las interfaces que admite la clase primaria.

IUnknown

Como se mencionó anteriormente, la versión de delegación de IUnknown es la misma para cada componente, ya que no hace nada más que invocar la instancia correcta de la versión no delegada. Por comodidad, el archivo de encabezado Combase.h contiene una macro, DECLARE_IUNKNOWN, que declara los tres métodos de delegación como métodos insertados. Se expande al código siguiente:

STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {      
    return GetOwner()->QueryInterface(riid,ppv);            
};                                                          
STDMETHODIMP_(ULONG) AddRef() {                             
    return GetOwner()->AddRef();                            
};                                                          
STDMETHODIMP_(ULONG) Release() {                            
    return GetOwner()->Release();                           
};

La función de utilidad CUnknown::GetOwner recupera un puntero a la interfaz IUnknown del componente que posee este componente. Para un componente agregado, el propietario es el componente externo. De lo contrario, el componente es el propietario. Incluya la macro DECLARE_IUNKNOWN en la sección pública de la definición de clase.

Constructor de clase

El constructor de clase debe invocar el método constructor para la clase primaria, además de cualquier cosa que haga que sea específica de la clase. El ejemplo siguiente es un método de constructor típico:

CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr) 
    : CUnknown(tszName, pUnk, phr)
{ 
    /* Other initializations */ 
};

El método toma los parámetros siguientes, que pasa directamente al método del constructor CUnknown .

  • tszName especifica un nombre para el componente.
  • pUnk es un puntero a la agregación de IUnknown.
  • pHr es un puntero a un valor HRESULT, que indica el éxito o error del método.

Resumen

En el ejemplo siguiente se muestra una clase derivada que admite IUnknown y una interfaz hipotética denominada ISomeInterface:

class CMyComponent : public CUnknown, public ISomeInterface
{
public:

    DECLARE_IUNKNOWN;

    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    {
        if( riid == IID_ISomeInterface )
        {
            return GetInterface((ISomeInterface*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr) 
        : CUnknown(tszName, pUnk, phr)
    { 
        /* Other initializations */ 
    };

    // More declarations will be added later.
};

En este ejemplo se muestran los siguientes puntos:

  • La clase CUnknown implementa la interfaz IUnknown . El nuevo componente hereda de CUnknown y de las interfaces que admita el componente. El componente podría derivarse en su lugar de otra clase base que hereda de CUnknown.
  • La macro DECLARE_IUNKNOWN declara los métodos IUnknown de delegación como métodos insertados.
  • La clase CUnknown proporciona la implementación de INonDelegatingUnknown.
  • Para admitir una interfaz distinta de IUnknown, la clase derivada debe invalidar el método NonDelegatingQueryInterface y probar el IID de la nueva interfaz.
  • El constructor de clase invoca el método constructor para CUnknown.

El siguiente paso para escribir un filtro es habilitar una aplicación para crear nuevas instancias del componente. Esto requiere una comprensión de los archivos DLL y su relación con generadores de clases y métodos constructores de clases. Para obtener más información, vea How to Create a DirectShow Filter DLL.

Cómo implementar IUnknown