Usando CUnknown

[O recurso associado a esta página, DirectShow, é um recurso herdado. Ele foi substituído por MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo na Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda fortemente que o novo código use MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]

O DirectShow implementa IUnknown em uma classe base chamada CUnknown. Você pode usar CUnknown para derivar outras classes, substituindo apenas os métodos que mudam entre componentes. A maioria das outras classes base no DirectShow derivam de CUnknown, para que seu componente possa herdar diretamente de CUnknown ou de outra classe base.

Inondelegatingunknown

CUnknown implementa INonDelegatingUnknown. Ele gerencia contagens de referência internamente e, na maioria das situações, sua classe derivada pode herdar os dois métodos de contagem de referência sem alteração. Lembre-se de que CUnknown se exclui quando a contagem de referência cai para zero. Por outro lado, você deve substituir CUnknown::NonDelegatingQueryInterface, pois o método na classe base retorna E_NOINTERFACE se receber qualquer IID diferente de IID_IUnknown. Em sua classe derivada, teste os IIDs de interfaces compatíveis, conforme mostrado no exemplo a seguir:

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);
}

A função de utilitário GetInterface (consulte COM Helper Functions) define o ponteiro, incrementa a contagem de referência de uma maneira thread-safe e retorna S_OK. No caso padrão, chame o método de classe base e retorne o resultado. Se você derivar de outra classe base, chame seu método NonDelegatingQueryInterface . Isso permite que você dê suporte a todas as interfaces compatíveis com a classe pai.

IUnknown

Conforme mencionado anteriormente, a versão de delegação do IUnknown é a mesma para todos os componentes, pois não faz nada mais do que invocar a instância correta da versão não desdlegante. Para conveniência, o arquivo de cabeçalho Combase.h contém uma macro, DECLARE_IUNKNOWN, que declara os três métodos de delegação como métodos embutidos. Ele se expande para o seguinte código:

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

A função de utilitário CUnknown::GetOwner recupera um ponteiro para a interface IUnknown do componente que possui esse componente. Para um componente agregado, o proprietário é o componente externo. Caso contrário, o componente possui a si mesmo. Inclua a macro DECLARE_IUNKNOWN na seção pública de sua definição de classe.

Construtor de classe

O construtor de classe deve invocar o método de construtor para a classe pai, além de tudo o que ele faz que seja específico para sua classe. O exemplo a seguir é um método de construtor típico:

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

O método usa os seguintes parâmetros, que ele passa diretamente para o método do construtor CUnknown .

  • tszName especifica um nome para o componente.
  • pUnk é um ponteiro para o IUnknown agregador.
  • pHr é um ponteiro para um valor HRESULT, indicando o êxito ou a falha do método.

Resumo

O exemplo a seguir mostra uma classe derivada que dá suporte a IUnknown e a uma interface hipotética chamada 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.
};

Este exemplo ilustra os seguintes pontos:

  • A classe CUnknown implementa a interface IUnknown . O novo componente herda de CUnknown e de qualquer interface compatível com o componente. O componente pode derivar de outra classe base que herda de CUnknown.
  • A macro DECLARE_IUNKNOWN declara os métodos IUnknown de delegação como métodos embutidos.
  • A classe CUnknown fornece a implementação para INonDelegatingUnknown.
  • Para dar suporte a uma interface diferente de IUnknown, a classe derivada deve substituir o método NonDelegatingQueryInterface e testar o IID da nova interface.
  • O construtor de classe invoca o método de construtor para CUnknown.

A próxima etapa na gravação de um filtro é habilitar um aplicativo para criar novas instâncias do componente. Isso requer uma compreensão das DLLs e sua relação com fábricas de classes e métodos de construtor de classe. Para obter mais informações, confira Como criar uma DLL de filtro DirectShow.

Como implementar o IUnknown