COM (Component Object Model) es un estándar creado por Microsoft que define a nivel binario como los objetos se deben crear, destruir e interactuar entre ellos. El hecho de que sea un estándar a nivel binario implica que es independiente del lenguaje que se use para trabajar con los objetos, siempre y cuando el lenguaje en cuestión sea capaz de manejar apuntadores a funciones.
Los componentes de software COM son independientes de cualquier aplicación y residirán en el sistema en forma de DLL (Dynamic Link Library). Cada componente
dispone de un identificador global único o GUID (Globally Unique Identifier) que lo caracteriza (este identificador consiste de un entero de 128 bits y se puede generar mediante el programa guidgen.exe). Si el componente está registrado en el sistema, entonces se podrá crear un objeto concreto de esa clase. Estar registrado en el sistema significa que el sistema operativo conoce donde reside "físicamente" la implementación de la clase identificada por el GUID. En el sistema Windows esta información se almacena en una base de datos centralizada llamada registro. Este registro se utiliza para configurar el software y hardware que se ejecute en el ambiente Windows. De esta manera, se consigue disponer de toda una librería de componentes que se podrá usar libremente, siempre y cuando estén registrados en el sistema.
Una interface es un conjunto de métodos (funciones) y atributos (datos) que tienen una fuerte relación lógica entre ellos. Un componente es la implementación de una o más interfaces y queda definida por las interfaces que implementa. La herencia de un componente se concibe a nivel de interfaces. Así todas las interfaces derivan de la interface IUnknown. Esta interface se encarga de la gestión de memoria de los objetos. La gestión de memoria se basa en un contador de referencias. La interface IUnknown está formada por tres métodos AddRef, Release y QueryInterface para controlar este contador. Las dos primeras permiten llevar la cuenta de si el componente está siendo utilizado y cuantos clientes lo están utilizando; de esta forma el sistema puede descargar un componente cuando no se esté utilizando. La tercera permite averiguar si el objeto en cuestión implementa una interface en particular o no, además de obtener un acceso al objeto a través de esa interface.
- 63 -
4.3.1
Creación y gestión de objetos
Los objetos se crean mediante la función CoCreateInstance, y se acceden a través de apuntadores a interfaces. Inicialmente, se obtiene un apuntador a una interface a raíz de la creación del propio objeto. Posteriormente, se puede acceder a las otras interfaces que se implementen con el método QueryInterface de ésta. Por ejemplo, si se tiene un componente identificado por el GUID GUID_MYCOMPONENT, que implementa las interfaces InterfaceA e InterfaceB se puede escribir el siguiente código:
InterfaceA *pIA; InterfaceB *pIB; CoInitialize(NULL);
CoCreateInstance(GUID_MYCOMPONENT, NULL, CLSCTX_INPROC, GUID_INTERFACEA, (LPVOID *) &pIA);
// Ahora se puede acceder a los métodos y atributos de la InterfaceA a través de pIA pIA->QueryInterface(GUID_INTERFACEB, (LPVOID *) &pIB);
// ... Ahora también se puede acceder a la InterfaceB a través de pIB pIA->Release();
// ... Ahora el objeto aún no se ha destruido porque aún se tiene una interfase activa pIB->Release();
// ... Ahora sí que se destruye el objeto CoUninitialize();
Entonces se puede decir que:
Primero, se tiene que comunicar al sistema que la aplicación va a ser cliente (o usuario) de objetos COM mediante la función CoInitialize, que se deberá
llamar al principio del programa. De la misma manera, hay que comunicar que se deja de ser clientes mediante CoUninitialize.
Por otro lado, se observa que las interfaces también tienen un GUID asociado que las identifica de forma universalmente única, es decir, no debería existir ninguna otra interface que tenga el mismo GUID.
El parámetro CLSCTX_INPROC especifica el contexto de ejecución del objeto, es decir, si se ejecutará dentro del mismo espacio de proceso de la aplicación, fuera de éste, etc. En general siempre se especificara que se ejecute dentro del proceso planteado.
Las referencias a CoCreateInstance y QueryInterface ya incrementan el contador de referencias, por lo que no es necesario hacerlo explícitamente con AddRef. En cambio, sí es necesario liberar los apuntadores a interface llamando a Release.
4.3.2
Gestión de errores
En el modelo COM se ha estandarizado la gestión de errores. Todas las funciones COM y métodos de objetos COM devuelven un valor de tipo HRESULT con un código de error. Si todo ha funcionado correctamente, este valor es S_OK. En cambio, si ha habido algún error, éste código dependerá de la función o método que sea invocado, ya que cada una puede generar tipos diferentes de errores. Se debe consultar la ayuda si se desea hacer un tratamiento exhaustivo de los diferentes tipos de errores.
En general, habrá que controlar como mínimo el resultado de la función CoCreateInstance y del método IUnknown::QueryInterface, ya que éstos devuelven
- 65 -
apuntadores a interfaces COM a través de las que accederemos al objeto. Si hay algún error en estos casos, el apuntador a interfase que se obtendra será erróneo, y en el momento en que se quiera llamar a alguno de sus métodos provocará un acceso a memoria no válido.
Explicado el funcionamiento de COM, se puede ver las características de DirectShow.