Red de conocimiento informático - Computadora portátil - Las diferencias entre componentes COM, objetos COM y estándares COM

Las diferencias entre componentes COM, objetos COM y estándares COM

En el estándar COM, un programa componente también se denomina módulo. Puede ser una biblioteca de vínculos dinámicos (DLL), denominada componente en proceso o puede ser un programa ejecutable (EXE). Se denomina componente fuera de proceso.

Los objetos COM se basan en el nivel de código ejecutable binario, mientras que en lenguajes como C++ los objetos se construyen en el nivel de código fuente, por lo que los objetos COM. son independientes del idioma. Esta característica permite interactuar con objetos componentes desarrollados en diferentes lenguajes de programación.

En la plataforma del sistema Microsoft Windows, la tecnología COM se aplica a todos los niveles del sistema, desde la gestión de objetos COM subyacente hasta la interacción de aplicaciones de capa superior, se utiliza el estándar COM.

Descripción general

COM no solo propone una especificación para la interacción entre componentes, sino que también proporciona un entorno para implementar la interacción, porque la especificación para la interacción entre objetos componentes no depende de ningún lenguaje específico. . , por lo que COM también puede ser un estándar para el desarrollo colaborativo de diferentes lenguajes.

La tecnología OLE se basa en la especificación COM. OLE aprovecha al máximo el estándar COM y hace que las aplicaciones del sistema operativo Windows sean extremadamente interactivas. Sin soporte OLE, el sistema operativo Windows sería muy inferior.

Sin embargo, la especificación COM no se limita a la tecnología OLE. De hecho, la tecnología OLE es solo una aplicación de COM. En los últimos años, la tecnología OLE ha mostrado grandes limitaciones en la interconexión de redes. fuerte adaptabilidad.

Los estándares COM incluyen dos partes: especificación e implementación. La parte de especificación define los componentes y el mecanismo de comunicación entre los componentes. Estas especificaciones no dependen de ningún lenguaje ni sistema operativo específicos. Se puede utilizar cualquier idioma; la parte de implementación del estándar COM es la biblioteca COM, que proporciona algunos servicios centrales para la implementación específica de la especificación COM.

COM es un modelo de software orientado a objetos, por lo que los objetos son uno de sus elementos básicos. De manera similar al concepto de objetos en C++, un objeto es una instancia de una clase; una clase es una definición de un grupo de datos y funciones relacionados. La aplicación que utiliza el objeto (u otro objeto) se convierte en el cliente y, a veces, en el usuario del objeto.

Una interfaz es un conjunto de funciones relacionadas lógicamente, y sus funciones también se denominan funciones miembro de la interfaz. Los objetos proporcionan diversas formas de servicios a los clientes a través de funciones miembro de la interfaz.

En el modelo COM, el objeto en sí es invisible para el cliente. Cuando el cliente solicita servicios, solo puede hacerlo a través de la interfaz. Cada interfaz se identifica mediante un identificador único global de 128 bits (GUID, Globally Unique Identifier). El cliente obtiene el puntero de la interfaz a través del GUID y, a través del puntero de la interfaz, el cliente puede llamar a su función miembro correspondiente.

En términos generales, la interfaz no cambia. Mientras la interfaz esperada por el cliente todavía exista en el objeto del componente, puede continuar utilizando los servicios proporcionados por la interfaz. Los objetos pueden admitir múltiples interfaces, por lo que se pueden actualizar los objetos componentes agregando interfaces, de modo que las nuevas interfaces obtenidas no afecten el uso de las interfaces antiguas.

¿Cómo identifican los clientes los objetos COM? De manera similar a las interfaces, cada objeto también se identifica mediante un GUID de 128 bits, llamado CLSID (identificador de clase, identificador de clase o ID de clase). El uso de CLSID para identificar objetos puede garantizar la unicidad global (en un sentido probabilístico).

Siempre que el sistema contenga información sobre este tipo de objeto COM, incluido el archivo del módulo (archivo DLL o EXE) donde se encuentra el objeto COM y el punto de entrada del objeto COM en el código, el El programa cliente puede utilizar este CLSID para crear un objeto COM.

Entonces, ¿cómo utilizan los clientes los servicios proporcionados por los objetos COM? ¿Qué obtienen los clientes?

De hecho, después de que el cliente crea exitosamente el objeto, lo que obtiene es un puntero a una interfaz del objeto. Debido a que el objeto COM implementa al menos una interfaz, el cliente puede llamar a todos los servicios proporcionados por el. interfaz.

Pero los objetos COM pueden tener su propio estado, y es este estado el que hace que los clientes sientan la existencia de los objetos COM. Si el cliente posee dos objetos con el mismo CLSID al mismo tiempo, los dos objetos pueden tener estados diferentes. El cliente no necesita preocuparse por cómo se implementa el objeto COM y cuál es la relación (matriz o lista vinculada) entre ellos. datos de estado de los dos objetos. Por supuesto, los objetos COM también pueden ser sin estado. Dichos objetos COM proporcionan principalmente servicios funcionales y pueden usarse para reemplazar las interfaces de funciones API tradicionales, lo que hace que la interfaz de programación de aplicaciones sea más ordenada y organizativamente jerárquica.

Además de la especificación, COM también tiene una parte de implementación, que incluye parte del código central a nivel del sistema. Es esta parte del código central la que permite que los objetos y los clientes se comuniquen a nivel de código binario. interfaces para interactuar.

En el entorno del sistema operativo Microsoft Windows, estas bibliotecas existen en forma de archivos .dll, incluidos los siguientes:

(1) Proporciona una pequeña cantidad de funciones API para implementar clientes y servicios El proceso de creación de una aplicación COM terminal. En el lado del cliente, proporciona principalmente algunas funciones de creación; en el lado del servidor, proporciona soporte de acceso para algunos objetos.

(2) COM busca en el servidor local, es decir, el programa EXE, a través del registro y convierte el nombre del programa a CLSID, etc.

(3) Proporciona algunos métodos de control de memoria estándar para permitir que las aplicaciones controlen la asignación de memoria en el proceso.

Las bibliotecas COM generalmente no se implementan en la capa de aplicación, sino en el nivel del sistema operativo, por lo que solo existe una implementación de biblioteca COM para un sistema operativo. Además, la implementación de la biblioteca COM debe depender de la plataforma específica del sistema, especialmente algunos estándares en la parte inferior del sistema.

La biblioteca COM puede garantizar que todos los componentes interactúen de manera unificada y nos permite usar aplicaciones COM directamente sin escribir una gran cantidad de código básico necesario para la comunicación COM. La biblioteca COM proporciona API para programación. acelerando así enormemente el desarrollo. Por ejemplo, la versión actual de la biblioteca COM admite componentes remotos, es decir, COM distribuido. Podemos lograr la comunicación entre programas en la red sin escribir ningún código de red o RPC (llamada a procedimiento remoto).

Si utilizamos un lenguaje orientado a objetos para implementar objetos COM, es natural utilizar clases para definir objetos. En lenguaje C, el concepto de objeto puede convertirse en un concepto lógico. Si existen dos objetos al mismo tiempo, la implementación de la interfaz debe saber claramente en qué objeto se realiza la operación. Este proceso puede garantizarse mediante la definición de la interfaz COM.

La idea de la especificación COM utilizando GUID para identificar objetos COM originados a partir de UUID (Universallz Unique Identifier) ​​adoptado por OSF (Open Software Foundation) se define como parte de DCE (Distributed Computing Environment). ) y se utiliza principalmente para Representa a ambas partes de la comunicación RPC.

Además de la encapsulación y la reutilización, otra característica importante de los objetos C++ es el polimorfismo. Es el polimorfismo de los objetos C++ el que encarna la característica altamente abstracta del lenguaje C++ que usa clases para describir cosas; los objetos COM también tienen polimorfismo, pero este polimorfismo debe reflejarse a través de las interfaces de los objetos COM. debe reflejarse a través de sus funciones (virtuales).

De API a interfaz COM

Si queremos implementar un sistema de aplicación de procesamiento de textos, necesitamos una función de búsqueda de diccionario. Según el método de programación del componente, naturalmente debemos buscar el. diccionario La función se implementa en un programa componente (.dll). Si el algoritmo de búsqueda o la biblioteca de diccionario del programa de diccionario cambia en el futuro, el sistema de aplicación aún puede utilizar el nuevo programa componente siempre que la interfaz entre el programa de aplicación y el componente permanezca sin cambios.

Ésta es la flexibilidad que conlleva el uso de programas de componentes.

Para conectar el sistema de aplicación y los programas componentes y permitirles trabajar juntos, la forma más sencilla es definir primero un conjunto de funciones de búsqueda de diccionario, y este conjunto de funciones debe ser lo más general posible sin añadiendo conocimientos específicos relacionados con las bibliotecas de diccionarios.

Función

Descripción de la función

Inicializar

Inicialización

Cargar Biblioteca

Cargar diccionario

InsertWord

Insertar una palabra

DeleteWord

Eliminar una palabra

LookupWord

Buscar palabras

RestoreLibrary

Almacenar el diccionario en la memoria en el archivo especificado

FreeLibrary

Liberar el diccionario

La capa de interfaz API plana puede conectar dos programas muy bien, pero hay algunos problemas:

(1) Cuando hay muchas funciones API, será muy incómodo de usar, las funciones deben usarse. organizar.

(2) Las funciones API deben estandarizarse y procesarse de acuerdo con un método de llamada unificado para adaptarse a diferentes implementaciones de programación de lenguajes. Es necesario estandarizar el orden de paso de los parámetros, los tipos de parámetros y el procesamiento de retorno de frío y calor.

COM define un conjunto completo de especificaciones de interfaz, que no solo pueden compensar las deficiencias de la API anterior como excusa de componente, sino que también aprovechan al máximo las ventajas de los objetos componentes y realizan el polimorfismo de objetos componentes.

Definición e identificación de interfaces

Técnicamente hablando, una interfaz es una estructura de datos que contiene un conjunto de funciones. A través de este conjunto de estructuras de datos, el código del cliente puede llamar a las funciones del componente. objeto. La interfaz define un conjunto de funciones miembro. Este conjunto de funciones miembro es toda la información expuesta por el objeto componente. El programa cliente utiliza estas funciones o los servicios del objeto componente.

El programa cliente utiliza un puntero a la estructura de datos de la interfaz para llamar a la función miembro de la interfaz. El puntero de la interfaz en realidad apunta a otro puntero. Este segundo puntero apunta a un conjunto de funciones, llamado tabla de funciones de la interfaz (tabla de funciones virtuales). Cada elemento de la tabla de funciones de la interfaz es un puntero de función de 4 bytes de longitud. a la implementación concreta del objeto. De esta manera, siempre que el cliente obtenga el puntero de la interfaz, puede llamar a la función real del objeto.

Para una interfaz, se determina su tabla de funciones virtuales vtable, por lo que el número de funciones miembro de la interfaz no cambia y el orden de las funciones miembro tampoco cambia para cada miembro. Para las funciones, sus parámetros y; También se determinan los valores de retorno.

En la definición de una interfaz, toda esta información debe determinarse a nivel binario, sin importar el idioma, siempre que pueda soportar dicha descripción de estructura de memoria, es decir, que pueda soportar el " tipo "estructura" o "registro", y este tipo puede contener miembros dobles que apuntan a la tabla de punteros de función, entonces puede admitir la descripción de la interfaz y puede usarse para escribir componentes COM o usar componentes COM.

Lenguaje de descripción de interfaz IDL

La especificación COM utiliza la especificación DCE de OSF para describir la interfaz de llamada remota IDL y luego la extiende para formar el lenguaje de descripción de la interfaz COM.

El lenguaje de descripción de interfaz IDL utilizado por la especificación COM no solo se puede usar para definir interfaces COM, sino que también define algunos tipos de datos de uso común y también puede describir estructuras de datos personalizadas. Para funciones miembro de la interfaz, podemos. especifica cada tipo de parámetro, características de entrada y salida, e incluso admite la descripción de matrices de longitud variable. IDL admite tipos de puntero, muy similares a C/C++.

Microsoft Visual C++ proporciona herramientas MIDL, que pueden compilar archivos de descripción de interfaz IDL en archivos de encabezado de descripción de interfaz compatibles con C/C++ (.h).

Definición de IUnknown (IDL):

interfaz IUnknown

{

HRESULT QueryInterface([en] REFIID iid, [salida] void **ppv);

ULONG AddRef(void);

ULONG Release(void);

}

Definición de IUnknown (C++):

clase IUnknown

{

Público:

virtual HRESULT _stdcall QueryInterface([en] REFIID iid, [salida ] void **ppv)=0;

virtual ULONG _stdcall AddRef(void)=0;

virtual ULONG _stdcall Release(void)=0;

}

Componente en proceso

Debido a que el componente en proceso y el programa cliente se ejecutan en el mismo espacio de direcciones de proceso, una vez que el programa cliente establece una relación de comunicación con el programa componente, el programa cliente obtiene El puntero de la interfaz apunta a la vtable de la interfaz en el programa componente. Esta vtable contiene las direcciones de todas las funciones miembro. El código del cliente puede llamar directamente a estas funciones miembro, por lo que su eficiencia es muy alta.

Debido a que el cliente carga el programa DLL en la memoria en tiempo de ejecución, el módulo DLL en sí es independiente y no depende del programa cliente.

En el lenguaje C++, para que el programa DLL compilado sea más versátil, generalmente se especifica que la función exportada del DLL usa la convención de llamada _stdcall. Si se usa la convención de llamada _cdecl, se requiere algo de programación. Los entornos de lenguaje no se pueden utilizar. El compilador de C++ genera un nombre decorado para cada función exportada del programa DLL. Estos nombres decorados no son compatibles con diferentes compiladores. Por lo tanto, desde la perspectiva de la versatilidad, agregamos ?C externo antes de cada definición de función. entorno de desarrollo, la siguiente declaración de descripción puede describir bien una función de exportación:

extern ? C“ int _stdcall MyFunction(int n);

Para compilar un programa DLL, podemos seguir estos pasos:

(1) Cree un proyecto DLL

(2) Para cada función exportada, utilice el especificador extern ? C" y el modificador _stdcall, como se describió anteriormente para MyFunction. función.

(3) De acuerdo con el método de programación tradicional, también debemos escribir un archivo DEF para describir la información del módulo del programa DLL en la plataforma Win32. No podemos usar el archivo DEF, pero. utilice directamente el especificador _declspec(dllexport) en la descripción de la función, por ejemplo:

extern ? C“_declspec(dllexport) int _stdcall MyFunction(int n);

El módulo DLL creado de esta manera otros programas pueden llamarlo, porque el vinculador de C++ conectará toda la información sobre las funciones exportadas al código de destino final.

Desde el lado del programa cliente, hay tres funciones del sistema que se pueden utilizar para operar programas DLL: LoadLibrary, GetProcAddress y FreeLibrary.

Generalmente, el uso de programas DLL sigue estos pasos:

Primero, el programa cliente usa la función LoadLibrary para cargar la DLL, que devuelve el identificador de instancia del módulo para su uso posterior. Utilice este módulo para operar.

Luego, el programa cliente puede llamar a la función GetProcAddress para obtener la dirección de la función exportada desde la DLL. Podemos obtener la dirección de la función exportada ya sea por el número de serie de la función (especificado en el archivo. Archivo DEF) o por el nombre de la función, debido a que el programa cliente y el programa DLL están en el mismo espacio de direcciones de memoria, el programa cliente puede llamar directamente a estas funciones exportadas.

Finalmente, FreeLibrary descarga el programa DLL de la memoria para liberar recursos.

Nota:

(1) El programa DLL no solo puede exportar funciones, sino también variables globales. Debido a que el programa cliente y el programa DLL están en el mismo espacio de direcciones, coloque el. Tiene sentido exportar variables globales a programas cliente. El método de citar no es complicado, coloque el nombre de la variable en la sección EXPORTACIONES del archivo DEF y agregue la opción DATOS o agregue el especificador _declspec (dllexport) delante de la descripción de la variable;

(2) DumpBin utiliza la opción /Exportaciones para enumerar toda la información exportada en el programa DLL.

(3) El programa cliente en sí también puede ser un programa DLL, pero primero debe cargarse en el espacio de proceso para que se pueda llamar a la función del sistema para operar el módulo DLL como programa de servicio.

Componentes fuera de proceso

Debido a que los programas de componentes fuera de proceso y los programas cliente están ubicados en diferentes espacios de proceso y utilizan diferentes espacios de direcciones, la comunicación entre el componente y el el cliente debe abarcar los límites del proceso, lo que implica las siguientes cuestiones:

(1) Cómo un proceso llama a una función en otro proceso

(2) Cómo se pasan los parámetros de un proceso a otro En un proceso

En la plataforma Windows, hay muchas formas de comunicarse entre diferentes procesos, incluido DDE, canalización con nombre o memoria compartida, etc. COM utiliza LPC (llamada a procedimiento local) y RPC (procedimiento remoto). Call)

RegEdit puede verificar el objeto COM bajo la subclave CLSID (página 63)

Microsoft Visual C++ proporciona OleView.exe, que puede enumerar todos los objetos en la máquina actual Información de categoría y una lista de objetos componentes en cada categoría.

RegSvr32 D:\DicComp\DictComp.dll

RegSvr32 /u D:\DicComp\DictComp.dll

El componente DLL debe tener dos DllRegisterServer y DllUnregisterServer La función de entrada utilizada para el registro se puede registrar con RegSvr32.

COM estipula que los componentes fuera de proceso que admiten el autoregistro deben admitir los dos parámetros de línea de comando /RegServer y /UnregServer para completar la operación de registro o cierre de sesión.

Class Factory

De hecho, el programa cliente no llama directamente a la función de exportación del programa componente, sino que llama a la función de la biblioteca COM para crear el objeto componente. La función de la biblioteca COM se basa en La información del registro llama a la función de entrada del programa componente para crear el objeto componente. El programa componente debe proporcionar una función de entrada estándar DLLGetObjectClass, que se utiliza para proporcionar información de componentes de este grupo de programas.

Fábrica de clases y función DLLGetObjectClass

La fábrica de clases es la base de producción de objetos COM. La biblioteca COM crea objetos COM a través de la fábrica de clases correspondiente a cada clase COM; Fábrica de clases dedicada a la operación de creación de objetos de esta clase COM.

La fábrica de clases en sí también es un objeto COM. Admite una interfaz especial: IClassFactory, que se define de la siguiente manera:

Clase IClassFactory: public IUnknown

{

virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0;

virtual HRESULT _stdcall LockServer(BOOL bLock) = 0;

};

La interfaz IClassFactory tiene una función miembro importante CreateInstance, que se utiliza para crear el objeto COM correspondiente. Debido a que cada generador de clases tiene como destino un objeto de clase COM específico, la función miembro CreateInstance sabe qué tipo de objeto COM crear. Entre los parámetros de la función miembro CreateInstance, el primer parámetro pUnknownOuter se usa cuando se agrega el objeto y se establece en NULL si no hay agregación. Otra función miembro de IClassFactory, LockServer, se utiliza para controlar el ciclo de vida del componente.

Debido a que el generador de clases en sí también es un objeto COM, se utiliza en el proceso de creación de otros objetos COM, entonces, ¿quién crea el objeto generador de clases? La respuesta es que la función de exportación DLLGetClassObject. La función DLLGetClassObject no es una función de la biblioteca COM, sino una función de exportación implementada por el programa componente. Veamos primero el prototipo de la función DLLGetClassObject:

HRESULT DLLGetClassObject(const CLSID& clsid,

Const IID& iid,

(void **) ppv

);

Después de recibir las instrucciones de creación del objeto, la biblioteca COM necesita ajustarse el componente en proceso La función DLLGetClassObject crea un objeto de fábrica de clases y devuelve el puntero de interfaz del objeto de fábrica de clases. Una vez que la biblioteca COM o el cliente tienen el puntero de interfaz de la fábrica de clases, pueden crear el objeto COM correspondiente a través de la función miembro. CreateInstance de la interfaz de fábrica de clases IClassFactory.

Interacción entre la biblioteca COM y la fábrica de clases (página 67)

En la biblioteca COM, hay tres funciones API que se pueden usar para la creación de objetos, son CoGetClassObject, CoCreateInstance y CoCreateInstanceEx. . Normalmente, el programa cliente llama a uno de ellos para completar la creación del objeto y devolver el puntero de interfaz inicial del objeto. La biblioteca COM y el generador de clases también interactúan a través de estas tres funciones.

¡Es muy superficial pensar en los componentes COM como archivos DLL!

DLL es una forma de publicación de componentes COM y un servidor de componentes.

¡Los componentes deben considerarse como un conjunto de interfaces implementadas en una DLL!

DLL es una forma, los componentes COM son la esencia.