El Modelo de Objetos Componentes ( COM ) es una tecnología de interfaz binaria para componentes de software de Microsoft que permite utilizar objetos de manera neutral respecto del lenguaje entre diferentes lenguajes de programación , contextos de programación, procesos y máquinas .
COM es la base de otras tecnologías de componentes específicos del dominio de Microsoft, entre las que se incluyen OLE , OLE Automation , ActiveX , COM+ y DCOM, así como implementaciones como DirectX , Windows shell , UMDF , Windows Runtime y Browser Helper Object .
COM permite el uso de objetos con solo conocer su interfaz, no su implementación interna. El implementador del componente define interfaces que son independientes de la implementación.
La compatibilidad con múltiples contextos de programación se gestiona apoyándose en el objeto para aspectos que serían difíciles de implementar como una función. La compatibilidad con múltiples usos de un objeto se gestiona exigiendo que cada objeto se destruya a sí mismo mediante el conteo de referencias . El acceso a las interfaces de un objeto (similar a la conversión de tipos ) también lo proporciona cada objeto.
COM está disponible únicamente en Microsoft Windows y en la interfaz de programación de aplicaciones (API) de complemento Core Foundation 1.3 y versiones posteriores de Apple . [1] Esta última solo implementa un subconjunto de toda la interfaz COM. [2]
Con el tiempo, COM se ha ido sustituyendo por otras tecnologías como Microsoft .NET y servicios web (es decir, a través de WCF ). Sin embargo, los objetos COM se pueden utilizar en un lenguaje .NET a través de COM Interop .
COM es similar a otras tecnologías de componentes como SOM , CORBA y Enterprise JavaBeans , aunque cada una tiene sus fortalezas y debilidades.
A diferencia de C++ , COM proporciona una interfaz binaria de aplicación (ABI) estable que no se ve afectada por las diferencias del compilador. [3] Esto hace que el uso de COM sea ventajoso para las bibliotecas C++ orientadas a objetos que serán utilizadas por clientes compilados a través de diferentes compiladores.
Introducido en 1987, el intercambio dinámico de datos (DDE) fue una de las primeras tecnologías de comunicación entre procesos en Windows . [4] [5] Permitió enviar y recibir mensajes en las llamadas conversaciones entre aplicaciones.
Antony Williams, involucrado en la arquitectura de COM, distribuyó dos artículos dentro de Microsoft que adoptaban el concepto de componentes de software: Object Architecture: Dealing With the Unknown – or – Type Safety in a Dynamically Extensible Class Library en 1988 y On Inheritance: What It Means and How To Use It en 1990. Estos proporcionaron la base de muchas de las ideas detrás de COM.
Object Linking and Embedding (OLE), el primer marco basado en objetos de Microsoft, se creó sobre DDE y se diseñó específicamente para documentos compuestos . Se introdujo con Word y Excel en 1991 y, más tarde, se incluyó en Windows, a partir de la versión 3.1 en 1992. Un ejemplo de documento compuesto es una hoja de cálculo incrustada en un documento de Word. A medida que se realizan cambios en la hoja de cálculo en Excel, aparecen automáticamente en el documento de Word.
En 1991, Microsoft introdujo la tecnología Visual Basic Extension (VBX) con Visual Basic 1.0. Una VBX es una extensión empaquetada en forma de biblioteca de vínculos dinámicos (DLL) que permite colocar objetos gráficamente en un formulario y manipularlos mediante propiedades y métodos . Estas fueron adaptadas posteriormente para su uso en otros lenguajes como Visual C++ .
En 1992, con Windows 3.1 , Microsoft lanzó OLE 2 con su nuevo modelo de objetos subyacente , COM. La interfaz binaria de aplicación COM (ABI) era la misma que la ABI MAPI (lanzada en 1992) y, al igual que ésta, se basaba en MSRPC y, en última instancia, en el DCE/RPC de Open Group . COM se creó para reemplazar a DDE, ya que su diseño de conversación basada en texto y mensajería de Windows no era lo suficientemente flexible como para permitir compartir características de la aplicación de una manera sólida y extensible.
En 1994 se introdujo la tecnología de control personalizado OLE (OCX), basada en COM, como sucesora de VBX. Al mismo tiempo, Microsoft anunció que OLE 2 se conocería simplemente como "OLE".
A principios de 1996, Microsoft encontró un nuevo uso para OCX: ampliar las capacidades de su navegador web. Microsoft renombró algunas partes de OLE relacionadas con Internet como ActiveX y gradualmente renombró todas las tecnologías OLE como ActiveX, excepto la tecnología de documento compuesto que se usaba en Microsoft Office .
Más tarde, en 1996, Microsoft amplió COM para que funcionara en toda la red con DCOM . [6]
El COM IDL se basa en el IDL DCE/RPC, rico en funciones, con extensiones orientadas a objetos. La implementación de DCE/RPC de Microsoft, MSRPC , se utiliza como el mecanismo principal de comunicación entre procesos para los servicios y componentes internos de Windows NT, lo que lo convierte en una opción obvia como base.
DCOM amplía COM desde el simple soporte de un único usuario con aplicaciones independientes que se comunican en el escritorio de Windows hasta la activación de objetos que se ejecutan en diferentes contextos de seguridad y en diferentes máquinas de la red. Con esto se añadieron las características necesarias para configurar qué usuarios tienen autoridad para crear, activar y llamar objetos, para identificar al usuario que llama y para especificar el cifrado necesario para la seguridad de las llamadas.
Microsoft introdujo Microsoft Transaction Server (MTS) en Windows NT 4 con el fin de proporcionar a los desarrolladores soporte para transacciones distribuidas , agrupación de recursos, aplicaciones desconectadas, publicación y suscripción de eventos, mejor gestión de memoria y procesador (subprocesos), así como para posicionar a Windows como una alternativa a otros sistemas operativos de nivel empresarial.
En Windows 2000, el conjunto de funciones se rebautizó como COM+ y se incorporó al sistema operativo en lugar de la serie de herramientas externas que proporcionaba MTS. Al mismo tiempo, Microsoft dejó de darle importancia a DCOM como entidad independiente. Los componentes que utilizaban COM+ se gestionaban de forma más directa mediante la capa añadida de COM+; en particular, mediante la compatibilidad del sistema operativo con la interceptación. En la primera versión de MTS, la interceptación se agregó: la instalación de un componente de MTS modificaba el Registro de Windows para llamar al software de MTS, y no al componente directamente.
Windows 2000 incluyó actualizaciones del panel de control de Servicios de componentes para configurar componentes COM+.
Una ventaja de COM+ era que podía ejecutarse en "granjas de componentes". Las instancias de un componente, si estaban codificadas correctamente, podían agruparse y reutilizarse mediante nuevas llamadas a su rutina de inicialización sin descargarlo de la memoria. Los componentes también podían distribuirse (llamarse desde otra máquina). COM+ y Microsoft Visual Studio proporcionaban herramientas para facilitar la generación de proxies del lado del cliente, por lo que, aunque se utilizaba DCOM para realizar la llamada remota, era fácil de hacer para los desarrolladores. COM+ también introdujo un mecanismo de eventos de suscriptor/publicador llamado COM+ Events y proporcionó una nueva forma de aprovechar MSMQ (una tecnología que proporciona mensajería asincrónica entre aplicaciones) con componentes llamados Queued Components . Los eventos COM+ amplían el modelo de programación COM+ para admitir eventos de enlace tardío (consulte Enlace tardío ) o llamadas de método entre el publicador o suscriptor y el sistema de eventos.
.NET es la tecnología de componentes de Microsoft que reemplaza a COM. .NET oculta muchos detalles de la creación de componentes y, por lo tanto, facilita el desarrollo.
.NET proporciona envoltorios para los controles COM comúnmente utilizados.
.NET puede aprovechar COM+ a través del System.EnterpriseServices
espacio de nombres, y varios de los servicios que proporciona COM+ se han duplicado en .NET. Por ejemplo, el System.Transactions
espacio de nombres proporciona la TransactionScope
clase, que permite la gestión de transacciones sin recurrir a COM+. De manera similar, los componentes en cola se pueden reemplazar por Windows Communication Foundation (WCF) con un transporte MSMQ .
Existe un soporte limitado para la compatibilidad con versiones anteriores. Un objeto COM puede usarse en .NET implementando un Runtime Callable Wrapper (RCW). [7] Los objetos NET que cumplen con ciertas restricciones de interfaz pueden usarse en objetos COM llamando a un COM Callable Wrapper (CCW). [8] Tanto desde el lado COM como desde el lado .NET, los objetos que usan la otra tecnología aparecen como objetos nativos. Consulte Interoperabilidad COM .
WCF facilita una serie de desafíos de ejecución remota de COM. Por ejemplo, permite ordenar objetos de manera transparente por valor en los límites de procesos o máquinas con mayor facilidad.
Windows Runtime ( WinRT ) es una API basada en COM, aunque es una variante de COM mejorada. Debido a su base similar a COM, WinRT admite la interconexión desde múltiples contextos de programación, pero es una API nativa no administrada. Las definiciones de API se almacenan en archivos ".winmd", que están codificados en formato de metadatos ECMA 335; el mismo formato de metadatos CLI que utiliza .NET con algunas modificaciones. Este formato de metadatos permite una sobrecarga significativamente menor que P/Invoke cuando se invoca WinRT desde aplicaciones .NET.
Nano-COM es un subconjunto de COM centrado en los aspectos de interfaz binaria de aplicación (ABI) de COM que permiten llamadas a funciones y métodos en módulos/componentes compilados de forma independiente. Nano-COM se puede expresar en un archivo de encabezado C++ portátil. Nano-COM extiende la ABI nativa de la arquitectura de instrucciones subyacente y el sistema operativo para admitir referencias de objetos tipificados, mientras que una ABI típica se centra en tipos atómicos, estructuras, matrices y convenciones de llamadas a funciones.
Un archivo de encabezado Nano-COM define o nombra al menos tres tipos:
dynamic_cast<T>
la adquisición de nuevos tipos de interfaz y el conteo de referencias al estilo de IUnknown.shared_ptr<T>
Muchos usos de Nano-COM definen dos funciones para abordar los buffers de memoria asignados al destinatario como resultados:
Algunas implementaciones de Nano-COM como Direct3D evitan las funciones de asignador y se limitan a utilizar únicamente los buffers asignados por el llamador.
Nano-COM no tiene nociones de clases, apartamentos, marshaling, registro, etc. En cambio, las referencias a objetos simplemente se pasan a través de los límites de las funciones y se asignan a través de construcciones de lenguaje estándar (por ejemplo, new
el operador C++).
La base de Nano-COM fue utilizada por Mozilla para arrancar Firefox (llamado XPCOM ), y actualmente se utiliza como tecnología ABI base para DirectX / Direct3D / DirectML .
Dado que un control ActiveX (cualquier componente COM) se ejecuta como código nativo, sin protección de sandbox , existen pocas restricciones sobre lo que puede hacer. El uso de componentes ActiveX, como los que admite Internet Explorer , en una página web puede provocar problemas de infecciones de malware . Microsoft reconoció el problema en 1996, cuando Charles Fitzgerald dijo: "Nunca afirmamos de antemano que ActiveX es intrínsecamente seguro". [9] Las versiones posteriores de Internet Explorer preguntan al usuario antes de instalar un control ActiveX, lo que les permite bloquear la instalación.
Como nivel de protección, un control ActiveX se firma con una firma digital para garantizar la autenticidad.
También es posible deshabilitar los controles ActiveX por completo o permitir solo unos pocos seleccionados.
El soporte transparente para servidores COM fuera de proceso promueve la seguridad del software en términos de aislamiento de procesos . Esto puede ser útil para desacoplar subsistemas de aplicaciones grandes en procesos separados. El aislamiento de procesos limita que la corrupción de estado en un proceso afecte negativamente la integridad de los otros procesos, ya que solo se comunican a través de interfaces estrictamente definidas. Por lo tanto, solo es necesario reiniciar el subsistema afectado para recuperar el estado válido. Este no es el caso de los subsistemas dentro del mismo proceso, donde un puntero no autorizado en un subsistema puede corromper aleatoriamente otros subsistemas.
COM se admite a través de enlaces en varios lenguajes, como C , C++ , Visual Basic , Delphi , Python [10] [11] y varios de los contextos de scripting de Windows. El acceso a los componentes se realiza a través de métodos de interfaz . Esto permite la llamada directa en proceso y a través del acceso del subsistema COM/DCOM entre procesos y computadoras.
Una coclase , una clase COM, implementa una o más interfaces. Se identifica mediante un identificador de clase, llamado CLSID , que es GUID , y mediante un identificador programático legible por humanos , llamado ProgID . Una coclase se crea mediante uno de estos identificadores.
Cada interfaz COM extiende la IUnknown
interfaz, que expone métodos para contar referencias y para acceder a las otras interfaces del objeto, similar a la conversión de tipos , también conocida como casting de tipos.
Una interfaz se identifica mediante un ID de interfaz (IID), un GUID.
Una interfaz personalizada , cualquier cosa derivada de IUnknown
, proporciona acceso anticipado mediante un puntero a una tabla de métodos virtuales que contiene una lista de punteros a las funciones que implementan las funciones declaradas en la interfaz, en el orden en que se declaran. Por lo tanto, una sobrecarga de invocación en proceso es comparable a una llamada a un método virtual de C++.
El envío, también conocido como acceso tardío , se proporciona mediante la implementación de IDispatch
. El envío permite el acceso desde una gama más amplia de contextos de programación que una interfaz personalizada.
Al igual que muchos lenguajes orientados a objetos, COM ofrece una separación entre la interfaz y la implementación. Esta distinción es especialmente marcada en COM, donde un objeto no tiene una interfaz predeterminada. Un cliente debe solicitar una interfaz para tener acceso. COM admite múltiples implementaciones de la misma interfaz, de modo que los clientes pueden elegir qué implementación de una interfaz utilizar.
Una biblioteca de tipos COM define metadatos COM, como coclases e interfaces. Una biblioteca se puede definir como lenguaje de definición de interfaz (IDL), una sintaxis independiente del lenguaje de programación. IDL es similar a C++ con sintaxis adicional para definir interfaces y coclases. IDL también admite atributos entre corchetes antes de las declaraciones para definir metadatos como identificadores y relaciones entre parámetros.
Un archivo IDL se compila a través del compilador MIDL. Para su uso con C/C++, el compilador MIDL genera un archivo de encabezado con struct
definiciones que coinciden con las tablas de variables de trabajo de las interfaces declaradas y un archivo C que contiene las declaraciones de los GUID de las interfaces . El compilador MIDL también puede generar el código fuente C++ para un módulo proxy. Este proxy contiene fragmentos de métodos para convertir las llamadas COM en llamadas a procedimientos remotos para habilitar DCOM para la comunicación fuera de proceso.
MIDL puede generar una biblioteca de tipos binarios (TLB) que puede ser utilizada por otras herramientas para admitir el acceso desde otro contexto.
El siguiente código IDL declara una coclase llamada SomeClass
que implementa una interfaz llamada ISomeInterface
.
coclass SomeClass { [predeterminado] interfaz ISomeInterface;};
Esto es conceptualmente equivalente al siguiente código C++ donde ISomeInterface es una clase virtual pura , también conocida como clase base abstracta.
clase ISomeInterface {}; clase SomeClass : público ISomeInterface { };
En C++, los objetos COM se instancian a través de la CoCreateInstance
función del subsistema COM que toma el CLSID y el IID. SomeClass
Se pueden crear de la siguiente manera:
ISomeInterface * interfaz_ptr = NULL ; HRESULT hr = CoCreateInstance ( CLSID_SomeClass , NULL , CLSCTX_ALL , IID_ISomeInterface , ( void ** ) & interfaz_ptr );
Un objeto COM utiliza el recuento de referencias para administrar la duración del objeto. El recuento de referencias de un objeto lo controlan los clientes a través de los métodos IUnknown
AddRef
y Release
. Los objetos COM son responsables de liberar su propia memoria cuando el recuento de referencias cae a cero. Algunos contextos de programación (por ejemplo, Visual Basic ) proporcionan un recuento de referencias automático para simplificar el uso de objetos. En C++, se puede utilizar un puntero inteligente para automatizar la administración del recuento de referencias.
Las siguientes son pautas sobre cuándo se debe llamar a AddRef y Release :
En el caso de los objetos remotos, no todas las llamadas de recuento de referencias se envían por cable. Un proxy conserva solo una referencia en el objeto remoto y mantiene su propio recuento de referencias local.
Para simplificar el desarrollo COM para los desarrolladores de C++, Microsoft introdujo ATL (Active Template Library) . ATL proporciona un paradigma de desarrollo COM de nivel relativamente alto. También protege a los desarrolladores de aplicaciones cliente COM de la necesidad de mantener directamente el recuento de referencias, al proporcionar tipos de punteros inteligentes . Otras bibliotecas y lenguajes que son compatibles con COM incluyen Microsoft Foundation Classes , VC Compiler COM Support, [12] VBScript , Visual Basic , ECMAScript ( JavaScript ) y Borland Delphi .
COM es un estándar binario independiente del lenguaje que permite utilizar objetos en cualquier contexto de programación capaz de acceder a sus interfaces binarias.
El software de cliente COM es responsable de habilitar el subsistema COM, crear instancias y contar referencias de objetos COM y consultar objetos para interfaces compatibles.
El compilador Microsoft Visual C++ admite extensiones del lenguaje C++, denominadas Atributos de C++ , [13] que están diseñadas para simplificar el desarrollo COM y minimizar el código repetitivo necesario para implementar servidores COM en C++. [14]
Originalmente, era necesario almacenar los metadatos de la biblioteca de tipos en el registro del sistema. Un cliente COM utilizaba la información del registro para la creación de objetos.
El COM sin registro (RegFree) se introdujo con Windows XP para permitir el almacenamiento de metadatos de bibliotecas de tipos como un manifiesto de ensamblaje, ya sea como un recurso en el archivo ejecutable o en un archivo separado instalado con el componente. [15] Esto permite instalar múltiples versiones del mismo componente en la misma computadora, en diferentes directorios. Y permite la implementación XCOPY . [16] Esta tecnología tiene soporte limitado para servidores COM EXE [17] y no se puede usar para componentes de todo el sistema como MDAC , MSXML , DirectX o Internet Explorer .
Durante la carga de la aplicación, el cargador de Windows busca el manifiesto. [18] Si está presente, el cargador agrega información de él al contexto de activación. [16] Cuando la fábrica de clases COM intenta crear una instancia de una clase, primero se verifica el contexto de activación para ver si se puede encontrar una implementación para el CLSID. Solo si la búsqueda falla, se escanea el registro . [16]
Un objeto COM puede crearse sin información de la biblioteca de tipos; con solo una ruta al archivo DLL y CLSID. Un cliente puede usar la función DLL COM DllGetClassObject
con CLSID e IID_IClassFactory para crear una instancia de un objeto de fábrica . El cliente puede luego usar el objeto de fábrica CreateInstance
para crear una instancia. [19] Este es el mismo proceso que usa el subsistema COM. [20] Si un objeto creado de esta manera crea otro objeto, lo hará de la manera habitual (usando el registro o manifiesto). Pero puede crear objetos internos (que pueden no estar registrados en absoluto) y entregarles referencias a interfaces, usando su propio conocimiento privado.
Un objeto COM se puede crear y utilizar de forma transparente desde el mismo proceso (en proceso), a través de los límites del proceso (fuera de proceso) o de forma remota a través de la red (DCOM). Los objetos fuera de proceso y remotos utilizan la serialización para serializar las llamadas a métodos y devolver valores a través de los límites del proceso o de la red. Esta serialización es invisible para el cliente, que accede al objeto como si fuera un objeto en proceso local.
En COM, el subprocesamiento se aborda a través de un concepto conocido como apartamentos . [21] Un objeto COM individual vive en exactamente un apartamento, que puede ser de un solo subproceso o de varios subprocesos. Hay tres tipos de apartamentos en COM: Apartamento de un solo subproceso (STA) , Apartamento de varios subprocesos (MTA) y Apartamento neutro de subprocesos (NA). Cada apartamento representa un mecanismo mediante el cual el estado interno de un objeto puede sincronizarse entre varios subprocesos. Un proceso puede constar de varios objetos COM, algunos de los cuales pueden usar STA y otros pueden usar MTA. Todos los subprocesos que acceden a objetos COM viven de manera similar en un apartamento. La elección del apartamento para los objetos y subprocesos COM se determina en tiempo de ejecución y no se puede cambiar.
Los subprocesos y objetos que pertenecen al mismo departamento siguen las mismas reglas de acceso a subprocesos. Por lo tanto, las llamadas a métodos que se realizan dentro del mismo departamento se realizan directamente sin ninguna asistencia de COM. Las llamadas a métodos que se realizan entre departamentos se logran mediante serialización. Esto requiere el uso de proxies y stubs.
COM es relativamente complejo, especialmente en comparación con tecnologías de componentes más modernas como .NET.
Cuando se inicializa una STA, se crea una ventana oculta que se utiliza para el enrutamiento de mensajes entre departamentos y entre procesos. Esta ventana debe tener su cola de mensajes "bombeada" regularmente. Esta construcción se conoce como " bombeo de mensajes ". En versiones anteriores de Windows, no hacerlo podría causar bloqueos en todo el sistema. Este problema se complica por algunas API de Windows que inicializan COM como parte de su implementación, lo que causa una "fuga" de detalles de implementación.
El recuento de referencias dentro de COM puede causar problemas si se hace referencia circular a dos o más objetos . El diseño de una aplicación debe tener esto en cuenta para que los objetos no queden huérfanos. Los objetos también pueden quedar con recuentos de referencias activos si se utiliza el modelo de "sumidero de eventos" de COM. Dado que el objeto que activa el evento necesita una referencia al objeto que reacciona al evento, el recuento de referencias de este último nunca llegará a cero. Los ciclos de referencia se interrumpen normalmente mediante la terminación fuera de banda o identidades divididas. En la técnica de terminación fuera de banda, un objeto expone un método que, cuando se llama, lo obliga a eliminar sus referencias a otros objetos, rompiendo así el ciclo. En la técnica de identidad dividida, una única implementación expone dos objetos COM separados (también conocidos como identidades). Esto crea una referencia débil entre los objetos COM, lo que impide un ciclo de referencia.
Dado que los componentes COM en proceso se implementan en archivos DLL y el registro solo permite una única versión por CLSID, en algunas situaciones pueden verse afectados por el efecto " DLL Hell ". La capacidad COM sin registro elimina este problema para los componentes en proceso; el COM sin registro no está disponible para los servidores fuera de proceso.
Si una llamada a la función CoGetClassObject encuentra el objeto de clase que se va a cargar en una DLL, CoGetClassObject utiliza la función DllGetClassObject exportada de la DLL.