| Feed: notas de monopolio Título: Notas de estudio sobre "Principios y aplicaciones de COM" | Autor: FGS_FGS Comentario |
| Notas de estudio de "Principios y aplicaciones de COM - PARTE 1 COM Principios http://savetime.delphibs.com Hora de inicio: 2004.1.30 Último modificado: 2004.2.1 El formato de este artículo es: El texto está envuelto automáticamente por la ventana; (El contenido de este artículo se extrae básicamente del libro "Principios y aplicaciones de com". Los derechos de autor son propiedad del autor Pan Aimin. Por favor, no lo use en los medios públicos) Tabla de contenido ==================================================== =================== ================================= ========= ⊙ Descripción general del capítulo 1 Que es com COM objetos e interfaces Modelo de proceso Com reutilización ⊙ Modelo de objetos del Capítulo 2 COM Identificador único a nivel mundial GUID COM Objeto Interfaz com Interface Descripción IDL IDL Interfaz Iunknown Principios de interfaz para objetos COM ⊙ Capítulo 3 Implementación de COM Información de registro de componentes com Registrar componentes com Class Factory y DllGetObjectClass Función CogetClassObject COCREATEINSTANCE / COCRATEINSTANTEEX FUNCIONES Inicialización de la biblioteca COM Gestión de memoria de la biblioteca COM Carga y desinstalación de programas de componentes Funciones comunes para la biblioteca com Tipo hresult ⊙ Características del Capítulo 4 COM Reutilización: inclusión y agregación Transparencia del proceso (para aprender) Seguridad (para aprender) Características múltiples (para aprender) ⊙ Capítulo 5 Desarrollo de aplicaciones COM con Visual C ++ Descripción de algunos archivos de encabezado proporcionados por Win32 SDK Algunas macros relacionadas con la interfaz COM ==================================================== =================== ================================= ========= texto ==================================================== =================== ================================= ========= ⊙ Descripción general del capítulo 1 ==================================================== =================== ================================= ========= Que es com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM es un estándar de componente propuesto por Microsoft. En el estándar COM, un programa de componentes también se llama módulo. Un programa de componentes puede contener uno o más objetos de componentes. ) es un portador de código que proporciona objetos COM. Los objetos COM son diferentes del concepto de objeto en los idiomas orientados a objetos generales (como C ++). Los objetos com son independientes del lenguaje. Es posible interactuar con los objetos de componentes desarrollados por diferentes lenguajes de programación. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM objetos e interfaces -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Similar al concepto de un objeto en C ++, un objeto es una instancia de una clase; Una aplicación que usa un objeto (u otro objeto) se llama cliente, a veces también se llama usuario del objeto. Una interfaz es un conjunto de funciones lógicamente relacionadas, y sus funciones también se denominan funciones de miembro de la interfaz. Según la costumbre, los nombres de interfaz a menudo tienen prefijo "I". Los objetos proporcionan a los clientes varias formas de servicios a través de funciones de miembros de la interfaz. En el modelo COM, el objeto en sí es invisible para el cliente, y cuando el cliente solicita el servicio, solo se puede realizar a través de la interfaz. Cada interfaz se identifica por un identificador único (GUID) de 128 bits (GUID). El cliente obtiene el puntero de la interfaz a través del GUID y luego pasa el puntero de la interfaz, y el cliente puede llamar a su función miembro correspondiente. Similar a las interfaces, cada componente también se identifica mediante un GUID de 128 bits, llamado CLSID (identificador de clase, identificador de clase o ID de clase). De hecho, después de que el cliente crea con éxito el objeto, obtiene un puntero a una interfaz del objeto. It. Todos los servicios. Según la especificación COM, si un objeto COM implementa múltiples interfaces, cualquier otra interfaz del objeto se puede obtener de una determinada interfaz. A partir de este proceso, también podemos ver que el cliente solo trata con el objeto COM a través de interfaces, y el objeto es solo un conjunto de interfaces para el cliente. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Modelo de proceso -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Los objetos de componentes de servicio proporcionados por COM tienen dos modelos de proceso al implementar: objetos en proceso y objetos fuera de proceso. Si se trata de un objeto en proceso, se ejecuta en el espacio de proceso del cliente; Programa de servicio en proceso: El programa de servicio se carga en el espacio de proceso del cliente. Programas de servicio local: El programa de servicio se ejecuta en la misma máquina que el programa del cliente. Programa de servicio remoto: El programa de servicio se ejecuta en una máquina diferente a la del cliente y puede ser un módulo DLL o un archivo EXE. Si el programa de servicio remoto se implementa en forma DLL, la máquina remota crea un proceso proxy. Aunque los objetos COM tienen diferentes modelos de proceso, esta diferencia es transparente para los programas del cliente. Sin embargo, al implementar objetos COM, aún debe elegir cuidadosamente el modelo de proceso. La ventaja de los modelos en proceso es que son eficientes, pero los componentes inestables harán que el proceso del cliente se bloquee, por lo que los componentes pueden poner en peligro al cliente; -El modelo de proceso también tendrá problemas, lo que puede deberse a que los componentes y los clientes en el proceso están en el mismo espacio de direcciones, ¿existe una gran posibilidad de conflicto? La estabilidad, y el proceso de componentes no pondrá en peligro el programa del cliente. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Com reutilización -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Dado que el estándar COM se basa en el nivel de código binario, la reutilización de los objetos COM es diferente del proceso de reutilización de los objetos en idiomas generales orientados a objetos, como C ++. Para el programa del cliente del objeto COM, solo utiliza los servicios proporcionados por el objeto a través de la interfaz, y no conoce el proceso de implementación dentro del objeto. En lugar de la implementación específica. Com utiliza dos mecanismos para realizar la reutilización de objetos. Suponemos que hay dos objetos COM, y el objeto 1 espera reutilizar la función del objeto 2. Llamamos al objeto 1 un objeto externo y objeto 2 un objeto interno. (1) forma inclusiva. El objeto 1 contiene el objeto 2. Cuando el objeto 1 necesita usar la función del objeto 2, simplemente puede entregar la implementación al objeto 2. Aunque el objeto 1 y el objeto 2 admiten la misma interfaz, el objeto 1 es al implementar la interfaz. , se llama a la implementación del objeto 2. (2) Método de agregación. El objeto 1 simplemente envía la interfaz del objeto 2 al cliente. existir. ==================================================== =================== ================================= ========= ⊙ Modelo de objetos del Capítulo 2 COM ==================================================== =================== ================================= ========= Identificador único a nivel mundial GUID -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La especificación COM utiliza un Identificador GUID único de 128 bits para identificar objetos e interfaces, que son números aleatorios y no requieren agencias especializadas para asignar y administrar. Debido a que GUID es un número aleatorio, la singularidad no está absolutamente garantizada, pero la posibilidad de que los identificadores se dupliquen es muy pequeña. En teoría, si una máquina genera 100,000,000 GUID por segundo, se puede garantizar que no se repetirán 3240 años (en el sentido de probabilidad). GUID se puede describir en C/C ++ utilizando esta estructura: typedef struct _guid { DATOS DWORD1; Data de palabras2; Data de palabras3; Byte data4 [8]; } GUID; Ejemplo: {64BF4372-1007-B0AA-444553540000} Puede definir un GUID de la siguiente manera: extern "c" const guid clsid_myspellchecker = {0x54bf0093, 0x1048, 0x399d, {0xb0, 0xa3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47}}; Visual C ++ proporciona dos programas para generar GUID: uuidgen.exe (línea de comandos) y guía.exe (diálogo). La biblioteca COM proporciona las siguientes funciones de API que pueden generar GUID: HRESULT COCRETEGUID (GUID *PGUID); Si GUID se crea con éxito, la función devuelve S_OK y PGUID puntos al valor de GUID resultante. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM Objeto -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- En la especificación COM, los objetos COM no están estrictamente definidos, pero COM proporciona un modelo de componentes orientado a objetos, y los componentes COM proporcionan a los clientes entidades encapsuladas en forma de objeto. La entidad en la que el programa del cliente interactúa con el programa COM es un objeto COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interfaz com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Técnicamente hablando, una interfaz es una estructura de datos que contiene un conjunto de funciones, a través de las cuales el código del cliente puede llamar a las funciones de los objetos de componentes. La interfaz define un conjunto de funciones miembros, que son toda la información expuesta por los objetos de componentes. Por lo general, llamamos a la tabla de funciones de la interfaz de la tabla de funciones virtuales (VTable), y el puntero al VTable es PVTable. Para una interfaz, se determina su tabla de funciones virtuales, por lo que el número de funciones miembros de la interfaz no cambia, y el orden de las funciones miembros tampoco está cambiado; En la definición de una interfaz, toda esta información debe determinarse a nivel binario. Interface Pointer ----> Pvtable ----> Función de puntero 1-> | ----------- | M_DATA1 Función del puntero 2 -> | M_DATA2 Función de puntero 3-> | ------------ | El primer parámetro de cada función de miembro de la interfaz es un puntero a la instancia del objeto (= esto). . Información, cuando se llama, la interfaz puede saber en qué objeto COM está funcionando. En las funciones de los miembros de la interfaz, las variables de cadena deben usar punteros de caracteres Unicode. Por lo tanto, si se usan caracteres ANSI dentro del programa de componentes, se deben convertir dos expresiones de caracteres. Por supuesto, en el caso de establecer programas de componentes y programas de clientes, puede usar los tipos de parámetros que se defina siempre que sean compatibles con los tipos de parámetros que COM puede reconocer. Visual C ++ proporciona dos conversiones de cadenas: espacio de nombres _com_util { Bstr ConvertStringTobstr (const char *psrc) throw (_com_error); BSTR ConvertBStrTtostring (BSTR PSRC) Show (_com_error); } BSTR es una cadena de doble byte-ancho, que es el tipo de datos automatizado más utilizado. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interface Descripción IDL IDL -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La especificación COM utiliza la especificación DCE de OSF para describir la interfaz de llamada remota IDL (lenguaje de descripción de la interfaz) y se expande para formar el lenguaje de descripción de la interfaz COM. Interface Descripción El lenguaje proporciona un método de descripción para interfaces que no dependen de ningún idioma, por lo que puede convertirse en un lenguaje común entre los programas de componentes y los programas de clientes. El lenguaje de descripción de la interfaz IDL utilizado por la especificación COM no solo puede usarse para definir la interfaz COM, sino que también definir algunos tipos de datos de uso común y estructuras de datos personalizadas. . IDL admite tipos de puntero, que son muy similares a C/C ++. Por ejemplo: Idiccionario de interfaz { HRESULT Initialize () HRESULT LoadLibrary ([en] cadena); HResult InsertWord ([in] cadena, [in] cadena); HRESULT DELETEWORD ([en] cadena); HResult Lookupword ([in] string, [out] string *); HResult RestorElibrary ([en] cadena); Hresult freelibrary (); } Microsoft Visual C ++ proporciona herramientas MIDL que pueden compilar archivos de descripción de la interfaz IDL en C/C ++-Interfaz compatible Descripción Archivos de encabezado (.h). -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interfaz Iunknown -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Definición IDL de Iunknown: interfaz iunknown { HRESULT QUERYIRFACE ([en] refiid iid, [out] void ** ppv); Ulong Addref (nulo); Liberación de ulong (nulo); } Definición de C ++ de Iunkown: Clase Iunknown { virus hResult _stdcall QueryInterface (const iid & iid, void ** ppv) = 0; Virtual Ulong _stdcall addref () = 0; virus ulong _stdcall libe () = 0; } -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Principios de interfaz para objetos COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La especificación COM establece las siguientes reglas para la función de consulta Interface: 1. Para diferentes punteros de la interfaz del mismo objeto, la interfaz Iunknown obtenida por consulta debe ser exactamente la misma. Es decir, el puntero de la interfaz Iunknown para cada objeto es único. Por lo tanto, para dos punteros de la interfaz, podemos determinar si apuntan al mismo objeto juzgando si las interfaces Iunknown que están consultando son iguales. 2. Reflexividad de la interfaz. La consulta de una interfaz en sí siempre debe tener éxito, por ejemplo: Pidictionary-> QueryInterface (IID_Dictionary, ...) debería devolver S_OK. 3. Simetría de interfaz. Si consulta de un puntero de interfaz a otro puntero de la interfaz, entonces regresar desde el segundo puntero de la interfaz al primer puntero de la interfaz debe tener éxito, por ejemplo: Pidictionary-> QueryInterface (iid_spellcheck, (void **) & PispellCheck); Si la búsqueda es exitosa, entonces consultar la interfaz IID_Dictionary desde PispellCheck definitivamente tendrá éxito. 4. Transitividad de la interfaz. Si consulta el puntero de la segunda interfaz desde el primer puntero de la interfaz y el puntero de la tercera interfaz se puede consultar desde el puntero de la segunda interfaz, entonces definitivamente puede consultar el primer puntero de interfaz desde el puntero de la tercera interfaz. 5. Tiempo de consulta de interfaz irrelevante. Si se puede encontrar un cierto puntero de la interfaz en un momento determinado, entonces el mismo puntero de la interfaz se verificará en cualquier momento en el futuro, y la consulta definitivamente tendrá éxito. En resumen, no importa de qué interfaz comenzamos, siempre podemos llegar a cualquier interfaz, y siempre podemos volver a la interfaz original. ==================================================== =================== ================================= ========= ⊙ Capítulo 3 Implementación de COM ==================================================== =================== ================================= ========= Información de registro de componentes com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Información sobre todos los componentes de la máquina actual HKEY_CLASS_ROOT/CLSID Componente en proceso HKEY_CLASS_ROOT/CLSID/GUID/INPROCSERVER32 Componente fuera de proceso HKEY_CLASS_ROOT/CLSID/GUID/Localserver32 Categoría a qué componente (CATID) HKEY_CLASS_ROOT/CLSID/GUID/Categorías implementadas Información de configuración de la interfaz COM HKEY_CLASS_ROOT/Interface Proxy dll/stub dll hkey_class_root/clsid/guía/proxystubclsid HKEY_CLASS_ROOT/CLSID/GUID/ProxyStubclsid32 Información sobre la biblioteca de tipos hkey_class_root/typelib String Naming Progid HKEY_CLASS_ROOT/ (por ejemplo "Comtl.Treectl") Componente GUID HKEY_CLASS_ROOT/COMTRL.TREECONTROL/CLSID Número de versión predeterminado HKEY_CLASS_ROOT/COMTRL.TREECONTROL/CURVER (por ejemplo, Curver = "Comtrl.Treectl.1", luego HKEY_CLASS_ROOT/COMTRL.TREECONTROL.1 también existe) Todas las categorías de componentes de la máquina actual HKEY_CLASS_ROOT/Categorías de componentes COM proporciona dos funciones API CLSIDFROMPROGID y PROGIDFROMCLSID para convertir progid y CLSID. Si el componente COM admite el mismo conjunto de interfaces, se pueden clasificar en la misma clase, y un componente puede clasificarse en múltiples clases. Por ejemplo, si todos los objetos de automatización admiten la interfaz IDISPATCH, se pueden clasificar como "objetos de automatización". La información de la categoría también es descrita por un GUID, llamado Catid. El objetivo principal de las categorías de componentes es que los clientes pueden descubrir rápidamente tipos específicos de objetos de componentes en la máquina, deben verificar todos los objetos de componentes, cargar los objetos de componentes en la memoria y instanciarlos, y luego preguntar si se implementan los necesarios. La interfaz, ahora utilizando categorías de componentes, puede guardar el proceso de consulta. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Registrar componentes com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- RegSRV32.Exe se utiliza para registrar un componente en proceso, que llama a las funciones de DllRegisterserver y DllunRegisterserver de la DLL para completar el registro y la cancelación del programa de componentes. Si la operación es exitosa, devuelva verdadero, de lo contrario, regrese falso. Para los programas de componentes fuera de proceso, la situación es ligeramente diferente porque es un programa ejecutable en sí y no puede proporcionar funciones de entrada para que otros programas los usen. Por lo tanto, la especificación COM estipula que los componentes fuera de proceso que admiten la autorregistra deben admitir dos parámetros de línea de comandos /regserver y /no regserver para completar las operaciones de registro y cancelación. Los parámetros de la línea de comandos dependen de la caja, y "/" se puede reemplazar por "-". Si la operación es exitosa, el programa devuelve 0, de lo contrario, devolver la falla de no 0 significa. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Class Factory y DllGetObjectClass -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Class Factory es la base de producción de los objetos COM. La fábrica de clases en sí también es un objeto COM, que admite una interfaz especial iClassFactory: clase iClassFactory: público iunknown { virtual hResult _stdcall createInstance (iunknown *punknownouter, const iid & iid, void ** ppv) = 0; virtual hResult _stdcall LockServer (Bool Block) = 0; } La función de miembro CrearInstance se usa para crear el objeto COM correspondiente. El primer parámetro PunknoWouter se usa para el caso donde la clase de objeto está agregada, y generalmente se establece en NULL; Interfaz Pointer. Las funciones miembros de LockServer se utilizan para controlar la vida útil de un componente. El objeto de fábrica de clases es creado por la función derivada de dll dllgetClassObject: HResult dllgetClassObject (const clsid & clsid, const iid & iid, (void **) ppv); El primer parámetro de la función dllgetClassObject es el CLSID del objeto que se creará. Debido a que un componente puede implementar múltiples clases de objetos COM, es necesario especificar el CLSID en los parámetros de la función dllgetClassObject para crear la fábrica de clase correcta. Los otros dos parámetros IID y PPV se refieren a la interfaz especificada IID y el puntero de la interfaz de fábrica de clase de almacenamiento, respectivamente. Después de recibir la instrucción para crear un objeto, la biblioteca COM llama a la función dllgetClassObject del componente en el proceso, crea el objeto de fábrica de clase desde la función y devuelve el puntero de interfaz del objeto de fábrica de clases. Una vez que una biblioteca o cliente de COM tiene un puntero de interfaz a la fábrica de clases, puede crear el objeto COM correspondiente a través de la función de miembro Crear Instance de iCLassFactory. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Función CogetClassObject -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- En la biblioteca COM, hay tres API que pueden usarse para la creación de objetos, a saber, CogetClassObject, CoCreateInstnace y CoCreateInstanceEx. Por lo general, el programa del cliente llama a uno de ellos para completar la creación del objeto y devuelve el puntero de interfaz inicial del objeto. La biblioteca COM y la fábrica de clases también interactúan a través de estas tres funciones. HRESULT COGETCLASSOBJECT (const clsid & clsid, dword dwclScontext, Coserverinfo *pServerinfo, const iid & iid, (void **) ppv); La función CogetClassObject primero encuentra la fábrica de clases de la clase COM especificada por CLSID, y luego se conecta al objeto de fábrica de clases. Si se trata de un objeto de componente en proceso, CogetClassObject llama al DllGetClassObject del módulo DLL para introducir la función, pasar los parámetros CLSID, IID y PPV a la función dllgetClassObject, y devuelve el puntero de interfaz del objeto de fábrica de clase. Normalmente, IID es el identificador IID_iclassFactory de iClassFactory. Si el objeto de fábrica de clase también admite otras interfaces que se pueden usar para crear operaciones, también se pueden usar otros identificadores de interfaz. Por ejemplo, se puede solicitar la interfaz iCLassFactory2 para verificar el estado de la licencia del usuario en el momento de la creación. La interfaz iCLassFactory2 es una extensión a iClassFactory, que mejora la seguridad de la creación de componentes. El parámetro DWCLSContext especifica la categoría de componente, que se puede especificar como un componente en proceso, un componente fuera de proceso o un objeto de control en proceso (similar al objeto proxy de un componente fuera de proceso, utilizado principalmente en tecnología ole). Los parámetros IID y PPV corresponden a los parámetros de dllgetClassObject, respectivamente, y se utilizan para especificar la interfaz IID y el puntero de la interfaz para almacenar el objeto de clase. El parámetro PServerInfo se usa para especificar la información del servidor al crear objetos remotos. La situación es mucho más complicada si el objeto de fábrica de clase creado por la función CogetClassObject se encuentra en un componente fuera de proceso. Primero, la función CogetClassObject inicia el proceso de componente y luego espera hasta que el proceso de componente registre la fábrica de clases del objeto de clase COM que admite a COM. Entonces, la función CogetClassObject devuelve la información de fábrica de clase correspondiente en com. Por lo tanto, cuando la biblioteca COM inicia un proceso fuera de componente (con el parámetro de la línea de comandos "/incrustación"), debe registrar los objetos de fábrica de clase COM compatibles en el COM a través de la función coreGisterClassObject para que la biblioteca COM pueda Crear objetos COM para usar. Cuando el proceso sale, se debe llamar a la función CoreVokeClassObject para notificar a COM que el objeto de fábrica de clases que registró ya no es válido. El programa de componentes llama a la función CoreGisterClassObject y la función CoreVokeClassObject debe emparejarse para garantizar la consistencia de la información de COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COCREATEINSTANCE / COCRATEINSTANTEEX FUNCIONES -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Hresult CoCreateInstance (const clsid & clsid, iunknown *punknownouter, DWord dwclScontext, const iid & iid, (void **) ppv); CoCreateInstance es una función de ayudante envuelta que realmente llama a la función CogetClassObject dentro de ella. El significado de los parámetros CLSID y DWCLSCONTEXT de coCreCreeInstance son consistentes con los parámetros correspondientes de CogetClassObject (los parámetros IID y PPV de la cosecha de co mayor son diferentes de CogetClassObject, uno es la información de interfaz que representa el objeto, y la otra es la información de interfaz que representa la clase fábrica). El parámetro PunknoWouter es consistente con los parámetros correspondientes en la creación de la interfaz de fábrica de clases, y se usa principalmente en el caso donde los objetos se agregan. La función CoCreateInstance encapsula el proceso de crear objetos a través de una fábrica de clase. CoCreateInstance se puede implementar con el siguiente código: (Nota de SaveTime: la aplicación del puntero de PPV en el siguiente código parece ser nulo **) Hresult CoCreateInstance (const clsid & clsid, iunknown *punknownouter, DWord dwclScontext, const iid & iid, void *ppv) { ICLassFactory *pcf; HRESULT HR; HR = CogetClassObject (CLSID, DWCLSCONTEXT, NULL, IID_ICLASSFACTORY, (nulo *) pcf); if (fallido (hr)) return hr; hr = pcf-> createInstance (punknownouter, iid, (void *) ppv); pfc-> liberar (); regresar HR; } Desde este código, podemos ver que la función de CoCreateInstance usa primero la función CogetClassObject para crear un objeto de fábrica de clase, y luego usa el puntero de interfaz del objeto de fábrica de clases para crear un objeto COM real. devuelto, de modo que la clase se usa. Sin embargo, el uso de CoCreateInstance no crea un objeto en la máquina remota, porque al llamar a CogetClassObject, el tercer parámetro utilizado para especificar la información del servidor se establece en NULL. Si desea crear un objeto remoto, puede usar la función de extensión de CoCreateInstance, CoCreateInstanceEx: HRESULT COCRATEINSTANTEEX (const clsid & clsid, iunknown *punknownouter, DWORD DWCLSCONTEXT, COSERVERINFO *PSERVERINFO, DWORD DWCOUNT, Multi_qi *rgmultiqi); Los primeros tres parámetros son los mismos que CoCreateInstance. Puntos de interfaz. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Inicialización de la biblioteca COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Antes de llamar a las funciones de la biblioteca COM, para que la función sea válida, se debe llamar a la función de inicialización de la biblioteca COM: HRESULT coinitialize (imalloc *pmalloc); PMALLOC se usa para especificar un asignador de memoria, y la aplicación puede especificar el principio de asignación de memoria. En general, si establecemos directamente el parámetro en NULL, la biblioteca COM utilizará el asignador de memoria proporcionado predeterminado. Valor de retorno: S_OK significa que la inicialización es exitosa S_FALSE indica que la inicialización es exitosa, pero esta llamada no es la primera vez que se llama a la función de inicialización en este proceso. S_UNEXPECTE indica que se produjo un error durante el proceso de inicialización y la aplicación no puede usar la biblioteca COM. Por lo general, un proceso inicializa la biblioteca COM solo una vez, y no tiene sentido inicializar la biblioteca COM varias veces en la misma unidad de módulo. La única función que no necesita inicializar la biblioteca COM es obtener la versión de la biblioteca COM: DWord COBUILDVERSION (); Valor de retorno: número de versión principal de 16 bits alto Número de versión inferior de 16 dígitos Después de que el programa COM utiliza el servicio de la biblioteca COM, generalmente antes de que salga el programa, es necesario llamar a la función de servicio de la biblioteca COM terminado para liberar los recursos mantenidos por la Biblioteca COM: nulo Couninicialize (nulo); Nota: Cualquier módulo de proceso o programa que llame a la función Coinitialize y devuelve S_OK debe tener una llamada de función de Councera correspondiente para garantizar que la biblioteca COM utilice efectivamente los recursos. (Si se llama a Coinitialize en un módulo y devuelve S_OK, ¿otros módulos que también están utilizando la biblioteca COM tendrán errores después de llamar a la función Councilización? ¿O la biblioteca COM verificará automáticamente qué módulos se usan?) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Gestión de memoria de la biblioteca COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Dado que los programas de componentes COM y los programas de clientes se conectan a través de los estándares de nivel binario, todas las operaciones que involucran interacciones de memoria entre clientes, bibliotecas y componentes COM (la asignación y la versión no están en el mismo módulo) en las aplicaciones COM deben usarse consistentes Administrador de memoria. El estándar de administración de memoria proporcionado por COM es en realidad una interfaz IMALLOC: // iid_imalloc: {00000002-0000-0000-C000-000000000046} clase imalloc: público iunknown { void * alloc (ulong cb) = 0; void * reasloc (void * pv, ulong cb) = 0; Void Free (void *pv) = 0; Ulong getize (void *pv) = 0; int didalloc (void *pv) = 0; void HeapMinimize () = 0; // Sistema operativo para la optimización del rendimiento } Obtenga el puntero de interfaz IMALLOC: HRESULT COGETMALLOC (DWORD DWMEMCONTEXT, IMALLOC ** PPMALLOC); El primer parámetro de la función CogetMalloc DWMEMContext se utiliza para especificar el tipo de administrador de memoria. La biblioteca COM contiene dos gerentes de memoria, uno es el administrador de memoria especificado durante la inicialización o su administrador predeterminado interno, también conocido como el administrador de trabajo (asignador de tareas). MEMCTX_TASK en el parámetro DWMEMContext; proceso y pasado a un segundo proceso, utilizando esta memoria en el segundo proceso o incluso liberándola. Mientras el valor de retorno de la función sea S_OK, PPMALLOC apunta al puntero de la interfaz de Memory Manager de la Biblioteca COM, y puede usarla para realizar operaciones de memoria. La biblioteca COM encapsula tres funciones API que se pueden usar para la asignación de memoria y la liberación: void * cotaskmemalloc (Ulong CB); void cotaskfree (void *pv); void cotaskMemRealloc (void *pv, ulong cb); Estas tres funciones se asignan a tres funciones miembros correspondientes a IMALLOC: ALLOC, REALELOC y Free. Ejemplo: Cómo un programa COM encuentra el valor de progid correspondiente del valor CLSID: Wchar *pwprogid; char pszprogid [128]; hResult = :: progidFromClSid (CLSID_DICCIONARY, & PWPROGID); if (hResult! = s_ok) { ... } WCStombs (PSZProgid, PwProgid, 128); Cotaskmemfree (pwprogid); Después de llamar a la función COM progidFromClSid para devolver, debido a que la biblioteca COM asigna el espacio de memoria para la variable de salida PWPROGID Este ejemplo ilustra una situación en la que la memoria se asigna en la biblioteca COM y la memoria se libera en el programa de llamadas. Algunas otras funciones en la biblioteca COM también tienen características similares, especialmente algunas funciones que contienen parámetros de salida de longitud indefinida. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Carga y desinstalación de programas de componentes -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 进程内组件的装载: 客户程序调用COM 库的CoCreateInstance 或CoGetClassObject 函数创建COM 对象,在CoGetClassObject 函数中,COM 库根据系统注册表中的信息,找到类标识符CLSID 对应的组件程序(DLL 文件)的全路径,然后调用LoadLibrary(实际上是CoLoadLibrary)函数,并调用组件程序的DllGetClassObject 引出函数。DllGetClassObject 函数创建相应的类厂对象,并返回类厂对象的IClassFactory 接口。至此CoGetClassObject 函数的任务完成,然后客户程序或者CoCreateInstance 函数继续调用类厂对象的CreateInstance 成员函数,由它负责COM 对象的创建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID -> DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace 进程外组件的装载: 在COM 库的CoGetClassObject 函数中,当它发现组件程序是EXE 文件(由注册表组件对象信息中的LocalServer 或LocalServer32 值指定)时,COM 库创建一个进程启动组件程序,并带上“/Embedding”命令行参数,然后等待组件程序;而组件程序在启动后,当它检查到“/Embedding”命令行参数后,就会创建类厂对象,然后调用CoRegisterClassObject 函数把类厂对象注册到COM 中。当COM 库检查到组件对象的类厂之后,CoGetClassObject 函数就把类厂对象返回。由于类厂与客户程序运行在不同的进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或COM 库得到了类厂对象,它就可以完成组件对象的创建工作。 进程内对象和进程外对象的不同创建过程仅仅影响了CoGetClassObject 函数的实现过程,对于客户程序来说是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy) 进程内组件的卸载: 只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为0,类厂的锁计数为0。满足这两个条件时,DllCanUnloadNow 引出函数返回TRUE。COM 提供了一个函数CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的DllCanUnloadNow 函数返回TRUE 时,就调用FreeLibrary 函数(实际上是CoFreeLibrary 函数)把该组件从程序从内存中卸出。 该由谁来调用CoFreeUnusedLibraries 函数呢?因为在组件程序执行过程中,它不可能把自己从内存中卸出,所以这个任务应该由客户来完成。客户程序随时都可以调用CoFreeUnusedLibraries 函数完成卸出工作,但通常的做法是,在程序的空闲处理过程中调用CoFreeUnusedLibraries 函数,这样做既可以避免程序中处处考虑对CoFreeUnusedLibraries 函数的调用,又可以使不再使用的组件程序得到及时清除,提高资源的利用率,COM 规范也推荐这种做法。 进程外组件的卸载: 进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在Windows 系统中,进程的主控函数为WinMain。 前面曾经说过,在组件程序启动运行时,它调用CoRegisterClassObject 函数,把类厂对象注册到COM 中,注册之后,类厂对象的引用计数始终大于0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的载条件与DllCanUnloadNow 中的判断类似,也需要判断COM 对象是否还存在、以及判断是否锁计数器为0,只有当条件满足了,进程的主函数才可以退出。 从原则上讲,进程外组件程序的卸载就是这么简单,但实际上情况可能复杂一些,因为有些组件程序在运行过程中可以创建自己的对象,或者包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可以增加一个“用户控制”标记flag,如果flag 为FALSE,则可以按简单的方法直接退出程序即可;如果flag 为TRUE,则表明用户参与了控制,组件进程不能马上退出,但应该调用CoRevokeClassObject 函数以便与CoRegisterClassObject 调用相响呼应,把进程留给用户继续进行。 如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可以采取两种办法:第一种方法,把应用隐藏起来,并把flag 标记设置为FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是,调用CoDisconnectObject 函数,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时可以也使用,以保证系统完成一些高优先级的操作。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM 库常用函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 初始化函数CoBuildVersion 获得COM 库的版本号 CoInitialize COM 库初始化 CoUninitialize COM 库功能服务终止 CoFreeUnusedLibraries 释放进程中所有不再使用的组件程序 GUID 相关函数IsEqualGUID 判断两个GUID 是否相等 IsEqualIID 判断两个IID 是否相等 IsEqualCLSID 判断两个CLSID 是否相等(*为什么要3个函数) CLSIDFromProgID 字符串组件标识转换为CLSID 形式 StringFromCLSID CLSID 形式标识转化为字符串形式 IIDFromString 字符串转换为IID 形式 StringFromIID IID 形式转换为字符串 StringFromGUID2 GUID 形式转换为字符串(*为什么有2) 对象创建函数CoGetClassObject 获取类厂对象 CoCreateInstance 创建COM 对象 CoCreateInstanceEx 创建COM 对象,可指定多个接口或远程对象 CoRegisterClassObject 登记一个对象,使其它应用程序可以连接到它 CoRevokeClassObject 取消对象的登记 CoDisconnectObject 断开其它应用与对象的连接 内存管理函数CoTaskMemAlloc 内存分配函数 CoTaskMemRealloc 内存重新分配函数 CoTaskMemFree 内存释放函数 CoGetMalloc 获取COM 库内存管理器接口 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- HRESULT 类型 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 大多数COM 函数以及一些接口成员函数的返回值类型均为HRESULT 类型。HRESULT 类型的返回值反映了函数中的一些情况,其类型定义规范如下: 31 30 29 28 16 15 0 |-----|--|------------------------|-----------------------------------| 类别码(30-31) 反映函数调用结果: 00 调用成功 01 包含一些信息 10 警告 11 错误 自定义标记(29) 反映结果是否为自定义标识,1 为是,0 则不是; 操作码(16-28) 标识结果操作来源,在Windows 平台上,其定义如下: #define FACILITY_WINDOWS 8 #define FACILITY_STORAGE 3 #define FACILITY_RPC 1 #define FACILITY_SSPI 9 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DISPATCH 2 #define FACILITY_CERT 11 操作结果码(0-15) 反映操作的状态,WinError.h 定义了Win32 函数所有可能返回结果。 以下是一些经常用到的返回值和宏定义: S_OK 函数执行成功,其值为0 (注意,其值与TRUE 相反) S_FALSE 函数执行成功,其值为1 S_FAIL 函数执行失败,失败原因不确定 E_OUTOFMEMORY 函数执行失败,失败原因为内存分配不成功 E_NOTIMPL 函数执行失败,成员函数没有被实现 E_NOTINTERFACE 函数执行失败,组件没有实现指定的接口 不能简单地把返回值与S_OK 和S_FALSE 比较,而要用SECCEEDED 和FAILED 宏进行判断。 ==================================================== =================== ================================= ========= ⊙ 第四章COM 特性 ==================================================== =================== ================================= ========= 可重用性:包容和聚合 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 包容模型: 组件对象在接口的实现代码中执行自身创建的另一个组件对象的接口函数(客户/服务器模型)。这个对象同时实现了两个(或更多)接口的代码。 聚合模型: 组件对象在接口的查询代码中把接口传递给自已创建的另一个对象的接口查询函数,而不实现该接口的代码。另一个对象必须实现聚合模型(也就是说,它知道自己正在被另一个组件对象聚合),以便QueryInterface 函数能够正常运作。 在组件对象被聚合的情况下,当客户请求它所不支持的接口或者请求IUnknown 接口时,它必须把控制交给外部对象,由外部对象决定客户程序的请求结果。 聚合模型体现了组件软件真正意义上的重用。 聚合模型实现的关键在CoCreateInstance 函数和IClassFactory 接口: HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); // class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0; 其中pUnknownOuter 参数用于指定组件对象是否被聚合。如果pUnknownOuter 参数为NULL,说明组件对象正常使用,否则说明被聚合使用,pUnknownOuter 是外部组件对象的接口指针。 聚合模型下的被聚合对象的引用计数成员函数也要进行特别处理。在未被聚合的情况下,可以使用一般的引用计数方法。在被聚合时,由客户调用AddRef/Release 函数时,必须转向外部组件对象的AddRef/Release 方法。这时,外部组件对象要控制被聚合的对象必须采用其它的引用计数接口。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 进程透明性(待学) 安全性(待学) 多线程特性(待学) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- ==================================================== =================== ================================= ========= ⊙ 第五章用Visual C++ 开发COM 应用 ==================================================== =================== ================================= ========= Win32 SDK 提供的一些头文件的说明 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Unknwn.h 标准接口IUnknown 和IClassFacatory 的IID 及接口成员函数的定义 Wtypes.h 包含COM 使用的数据结构的说明 Objidl.h 所有标准接口的定义,即可用于C 语言风格的定义,也可用于C++ 语言 Comdef.h 所有标准接口以及COM 和OLE 内部对象的CLSID ObjBase.h 所有的COM API 函数的说明 Ole2.h 所有经过封装的OLE 辅助函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 与COM 接口有关的一些宏 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- DECLARE_INTERFACE(iface) 声明接口iface,它不从其他的接口派生 DECLARE_INTERFACE_(iface, baseiface) 声明接口iface,它从接口baseiface 派生 STDMETHOD(method) 声明接口成员函数method,函数返回类型为HRESULT STDMETHOD_(type, method) 声明接口成员函数method,函数返回类型为type ==================================================== =================== ================================= ========= ⊙ 结束 ==================================================== =================== ================================= ========= |