Compartilhar via


Como o IUnknown funciona

[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.]

Os métodos no IUnknown permitem que um aplicativo consulte interfaces no componente e gerencie a contagem de referência do componente.

Contagem de Referência

A contagem de referência é uma variável interna, incrementada no método AddRef e decrementada no método Release . As classes base gerenciam a contagem de referência e sincronizam o acesso à contagem de referência entre vários threads.

Consultas de interface

A consulta de uma interface também é simples. O chamador passa dois parâmetros: um IID (identificador de interface) e o endereço de um ponteiro. Se o componente der suporte à interface solicitada, ele definirá o ponteiro para a interface, incrementará sua própria contagem de referência e retornará S_OK. Caso contrário, ele define o ponteiro como NULL e retorna E_NOINTERFACE. O pseudocódigo a seguir mostra a estrutura de tópicos geral do método QueryInterface . A agregação de componentes, descrita na próxima seção, introduz alguma complexidade adicional.

if (IID == IID_IUnknown)
    set pointer to (IUnknown *)this
    AddRef
    return S_OK

else if (IID == IID_ISomeInterface)
    set pointer to (ISomeInterface *)this
    AddRef
    return S_OK

else if ... 

else
    set pointer to NULL
    return E_NOINTERFACE

A única diferença entre o método QueryInterface de um componente e o método QueryInterface de outro é a lista de IIDs que cada componente testa. Para cada interface compatível com o componente, o componente deve testar o IID dessa interface.

Agregação e delegação

A agregação de componentes deve ser transparente para o chamador. Portanto, a agregação deve expor uma única interface IUnknown , com o componente agregado adiando para a implementação do componente externo. Caso contrário, o chamador verá duas interfaces IUnknown diferentes na mesma agregação. Se o componente não for agregado, ele usará sua própria implementação.

Para dar suporte a esse comportamento, o componente deve adicionar um nível de indireção. Um IUnknown delega o trabalho para o local apropriado: para o componente externo, se houver um, ou para a versão interna do componente. Um IUnknown não desdlegante faz o trabalho, conforme descrito na seção anterior.

A versão delegada é pública e mantém o nome IUnknown. A versão não desdlegante é renomeada como INonDelegatingUnknown. Esse nome não faz parte da especificação COM, pois não é uma interface pública.

Quando o cliente cria uma instância do componente, ele chama o método IClassFactory::CreateInstance . Um parâmetro é um ponteiro para a interface IUnknown do componente agregador ou NULL se a nova instância não for agregada. O componente usa esse parâmetro para armazenar uma variável de membro que indica qual interface IUnknown usar, conforme mostrado no exemplo a seguir:

CMyComponent::CMyComponent(IUnknown *pOuterUnkown)
{
    if (pOuterUnknown == NULL)
        m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;
    else
        m_pUnknown = pOuterUnknown;

    [ ... more constructor code ... ]
}

Cada método na delegação de IUnknown chama seu equivalente não desdlegante, conforme mostrado no exemplo a seguir:

HRESULT QueryInterface(REFIID iid, void **ppv) 
{
    return m_pUnknown->QueryInterface(iid, ppv);
}

Pela natureza da delegação, os métodos de delegação parecem idênticos em cada componente. Somente as versões não desdlegantes são alteradas.

Como implementar o IUnknown