QueryInterface: Navegando em um objeto
Depois de ter um ponteiro inicial para uma interface em um objeto, o COM tem um mecanismo muito simples para descobrir se o objeto suporta outra interface específica e, em caso afirmativo, para obter um ponteiro para ele. (Para obter informações sobre como obter um ponteiro inicial para uma interface em um objeto, consulte Obtendo um ponteiro para um objeto.) Esse mecanismo é o métodoQueryInterface da interfaceIUnknown. Se o objeto suportar a interface solicitada, o método deve retornar um ponteiro para essa interface. Isso permite que um objeto navegue livremente pelas interfaces suportadas por um objeto. QueryInterface separa a solicitação "Você apoia um determinado contrato?" do uso de alto desempenho desse contrato depois que as negociações forem bem-sucedidas.
Quando um cliente inicialmente obtém acesso a um objeto, esse cliente receberá, no mínimo, um ponteiro de interface IUnknown (a interface mais fundamental) através do qual ele pode controlar o tempo de vida do objeto — informando o objeto quando terminar de utilizá-lo — e invocar QueryInterface. O cliente é programado para solicitar a cada objeto que ele gere executar algumas operações, mas a interface IUnknown não tem funções para essas operações. Em vez disso, essas operações são expressas através de outras interfaces. O cliente é assim programado para negociar com objetos para essas interfaces. Especificamente, o cliente chamará QueryInterface para solicitar a um objeto uma interface através da qual o cliente pode invocar as operações desejadas.
Como o objeto implementa QueryInterface, ele tem a capacidade de aceitar ou rejeitar a solicitação. Se o objeto aceitar a solicitação do cliente, QueryInterface retornará um novo ponteiro para a interface solicitada para o cliente. Através desse ponteiro de interface, o cliente tem acesso aos métodos dessa interface. Se, por outro lado, o objeto rejeitar a solicitação do cliente, QueryInterface retornará um ponteiro nulo — um erro — e o cliente não terá um ponteiro através do qual chamar as funções desejadas. Neste caso, o cliente deve lidar graciosamente com essa possibilidade. Por exemplo, suponha que um cliente tenha um ponteiro para a interface A em um objeto e peça as interfaces B e C. Suponha também que o objeto suporta a interface B, mas não suporta a interface C. O resultado é que o objeto retorna um ponteiro para B e informa que C não é suportado.
Um ponto-chave é que, quando um objeto rejeita uma chamada para QueryInterface, é impossível para o cliente pedir ao objeto para executar as operações expressas através da interface solicitada. Um cliente deve ter um ponteiro de interface para invocar métodos nessa interface. Se o objeto se recusar a fornecer o ponteiro solicitado, o cliente deve estar preparado para prescindir, seja não fazendo o que pretendia fazer com esse objeto ou tentando recorrer a outra interface, talvez menos poderosa. Esse recurso da funcionalidade COM funciona bem em comparação com outros sistemas orientados a objetos nos quais você não pode saber se uma função funcionará até chamar essa função e, mesmo assim, lidar com a falha é incerto. QueryInterface fornece uma maneira confiável e consistente de saber se um objeto suporta uma interface antes de tentar chamar seus métodos.
O métodoQueryInterface também fornece uma maneira robusta e confiável para um objeto indicar que ele não suporta um determinado contrato. Ou seja, se em uma chamada para QueryInterface alguém perguntar a um objeto "antigo" se ele suporta uma interface "nova" (uma, por exemplo, que foi inventada depois que o objeto antigo foi enviado), o objeto antigo responderá "não" de forma confiável, sem causar uma falha. A tecnologia que suporta isso é o algoritmo pelo qual os IIDs são alocados. Embora isso possa parecer um pequeno ponto, é extremamente importante para a arquitetura geral do sistema, e a capacidade de indagar elementos legados sobre novas funcionalidades é, surpreendentemente, um recurso não presente na maioria das outras arquiteturas de objetos.
Tópicos relacionados