Como funciona o IUnknown
[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEnginee Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda vivamente que o novo código utilize 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 em 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 diminuída 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
Pesquisar uma interface também é simples. O chamador passa dois parâmetros: um identificador de interface (IID) e o endereço de um ponteiro. Se o componente suportar a interface solicitada, ele define o ponteiro para a interface, incrementa sua própria contagem de referência e retorna S_OK. Caso contrário, define o ponteiro como NULL e retorna E_NOINTERFACE. O pseudocódigo que se segue mostra o esquema 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 suportada pelo 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, em que o componente agregado delega à implementação do componente externo. Caso contrário, o chamador veria duas interfaces diferentes IUnknown no mesmo agregado. 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 delegando IUnknown delega o trabalho para o local apropriado: para o componente externo, se houver, ou para a versão interna do componente. Um não delegando IUnknown 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 delegada foi renomeada para INonDelegatingUnknown. Este nome não faz parte da especificação COM, porque não é uma interface pública.
Quando o cliente cria uma instância do componente, ele chama o IClassFactory::CreateInstance método. Um parâmetro é um ponteiro para a interface IUnknown 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 membro indicando qual IUnknown interface 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 no IUnknown de delegante chama sua contraparte não delegante, 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 todos os componentes. Apenas as versões não delegadas mudam.
Tópicos relacionados