QueryInterface: navegar por un objeto

Después de tener un puntero inicial a una interfaz en un objeto, COM tiene un mecanismo muy sencillo para averiguar si el objeto admite otra interfaz específica y, si es así, para obtener un puntero a él. (Para obtener información sobre cómo obtener un puntero inicial a una interfaz en un objeto, vea Obtener un puntero a un objeto). Este mecanismo es el método QueryInterface de la interfaz IUnknown . Si el objeto admite la interfaz solicitada, el método debe devolver un puntero a esa interfaz. Esto permite que un objeto navegue libremente por las interfaces que admite un objeto. QueryInterface separa la solicitud "¿Admite un contrato determinado?" del uso de alto rendimiento de ese contrato una vez que las negociaciones se hayan realizado correctamente.

Cuando un cliente obtiene inicialmente acceso a un objeto , ese cliente recibirá, como mínimo, un puntero de interfaz IUnknown (la interfaz más fundamental) a través del cual puede controlar la duración del objeto , indicando al objeto cuando se haya terminado mediante el objeto e invoque QueryInterface. El cliente está programado para pedir a cada objeto que administra realizar algunas operaciones, pero la interfaz IUnknown no tiene funciones para esas operaciones. En su lugar, esas operaciones se expresan a través de otras interfaces. Por lo tanto, el cliente está programado para negociar con objetos para esas interfaces. En concreto, el cliente llamará a QueryInterface para solicitar un objeto para una interfaz a través de la cual el cliente puede invocar las operaciones deseadas.

Dado que el objeto implementa QueryInterface, tiene la capacidad de aceptar o rechazar la solicitud. Si el objeto acepta la solicitud del cliente, QueryInterface devuelve un nuevo puntero a la interfaz solicitada al cliente. A través de ese puntero de interfaz, el cliente tiene acceso a los métodos de esa interfaz. Si, por otro lado, el objeto rechaza la solicitud del cliente, QueryInterface devuelve un puntero nulo (un error) y el cliente no tiene ningún puntero a través del cual llamar a las funciones deseadas. En este caso, el cliente debe ocuparse correctamente de esa posibilidad. Por ejemplo, supongamos que un cliente tiene un puntero a la interfaz A en un objeto y solicita las interfaces B y C. Suponga también que el objeto admite la interfaz B, pero no admite la interfaz C. El resultado es que el objeto devuelve un puntero a B e informa de que C no se admite.

Un punto clave es que, cuando un objeto rechaza una llamada a QueryInterface, es imposible que el cliente pida al objeto que realice las operaciones expresadas a través de la interfaz solicitada. Un cliente debe tener un puntero de interfaz para invocar métodos en esa interfaz. Si el objeto se niega a proporcionar el puntero solicitado, el cliente debe estar preparado para hacerlo sin hacerlo, ya sea sin hacer lo que tenía pensado hacer con ese objeto o intentando revertir a otra interfaz, tal vez menos potente. Esta característica de la funcionalidad COM funciona bien en comparación con otros sistemas orientados a objetos en los que no se puede saber si una función funcionará hasta que se llame a esa función y, incluso después, no se puede controlar el error. QueryInterface proporciona una manera confiable y coherente de saber si un objeto admite una interfaz antes de intentar llamar a sus métodos.

El método QueryInterface también proporciona una forma sólida y confiable de que un objeto indique que no admite un contrato determinado. Es decir, si en una llamada a QueryInterface se pregunta a un objeto "antiguo" si admite una interfaz "nueva" (una, por ejemplo, que se inventó después de que se hubiera enviado el objeto antiguo), el objeto antiguo será confiable, sin causar un bloqueo, responda "no". La tecnología que admite esto es el algoritmo por el que se asignan los IID. Aunque esto puede parecer un punto pequeño, es muy importante para la arquitectura general del sistema, y la capacidad de consultar elementos heredados sobre la nueva funcionalidad es, sorprendentemente, una característica que no está presente en la mayoría de las otras arquitecturas de objetos.

Uso e implementación de IUnknown