Portar COM a Linux

Me gusta la tecnología COM. Pero no hablaremos de tecnología, elogios o deficiencias de COM, sino de la experiencia de portar e implementar en Linux. Una bicicleta? Expediencia? No nos centremos en esto.


Objeto COM (1)

En términos generales, un objeto de una clase que implementa al menos una interfaz COM. La implementación del objeto se oculta principalmente en una biblioteca conectada dinámicamente llamada servidor COM (2) , las interfaces se publican y distribuyen para su uso.


Interfaz COM, una clase abstracta que contiene solo funciones virtuales puras. Se resalta una interfaz IUnknown especial, cualquier objeto COM debe implementar esta interfaz.


Cada interfaz COM debe contener su propio identificador. En COM, está determinado por la estructura del GUID y aquí enfrentaremos el primer inconveniente de COM. El GUID es incomprensible y no se lee bien, y todo lo demás descrito en el Wiki. Lo necesitamos igual, pero de una manera más legible y comprensible (llamémoslo uiid).


I Desconocido y uiid

#define define_uiid(name) \ inline static const std::string& guid() { const static std::string idn(dom_guid_pre_name #name); return idn; } namespace Dom { using uiid = std::string; using clsuid= std::string; struct IUnknown { virtual long AddRef() = 0; virtual long Release() = 0; virtual bool QueryInterface(const uiid&, void **ppv) = 0; define_uiid(Unknown) }; } 

Además del identificador de interfaz, también se asigna el identificador de clase (clsuid), que es necesario para crear el objeto. En nuestro caso, porque este es un identificador menos legible que puede determinar la esencia, puede olvidarse de su publicación por ahora (tal vez esto no sea bueno).


Resumen
Un objeto COM que contiene un identificador de clase única. Implementa al menos una interfaz COM: IUnknown (cualquier interfaz COM tiene un identificador de interfaz único). Las diferentes implementaciones de un objeto COM pueden tener el mismo identificador de clase (ejemplo: versión de lanzamiento y depuración).



Servidor COM (2)

Una biblioteca vinculada dinámicamente (para Linux es un objeto compartido, por lo tanto) que implementa al menos un objeto COM. El servidor debe exportar un conjunto específico de funciones:


 extern "C" bool DllCreateInstance(const uiid& iid, void** ppv) 
Crea un objeto de clase por clsuid, aumenta el número de referencias a eso, cada vez que el objeto se crea con éxito. La llamada a IUnknown :: AddRef también debería aumentar el recuento de referencias para eso, y IUnknown :: Release debería disminuir.

 extern "C" bool DllCanUnloadNow() 

Si el número de referencias a SO es 0, puede descargar la biblioteca.

 extern "C" bool DllRegisterServer(IUnknown* unknown) 

Registra todos los servidores clsuid en el "registro". Llamado una vez durante la instalación del servidor COM.

 extern "C" bool DllUnRegisterServer(IUnknown* unknown) 

Elimina de las entradas de "registro" sobre el servidor clsuid registrado. Llamado una vez al desinstalar el servidor COM.

Ejemplo de SimpleHello, declare la interfaz IHello:

 struct IHello : public virtual Dom::IUnknown { virtual void Print() = 0; define_uiid(Hello) }; 

Implementación de interfaz:

 /* COM- */ class SimpleHello : public Dom::Implement<SimpleHello, IHello> { public: SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } ~SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } virtual void Print() { printf("Hello from %s\n",__PRETTY_FUNCTION__); } define_clsuid(SimpleHello) }; /* COM- */ namespace Dom { DOM_SERVER_EXPORT_BEGIN EXPORT_CLASS(SimpleHello) DOM_SERVER_EXPORT_END DOM_SERVER_INSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } DOM_SERVER_UNINSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } } 

Un conjunto de macros oculta implementaciones de funciones al proporcionar una declaración y lógica más estructuradas.


Dom :: Implementar <SimpleHello, IHello>: oculta la implementación de los métodos de interfaz IUnknown, agrega "azúcar" al declarar interfaces implementadas por un objeto (C ++ 11 y plantillas variadas):



 template <typename T, typename ... IFACES> struct Implement : virtual public IUnknown, virtual public IFACES… { ... }; 

Interfaz IRegistryServer: define un conjunto de métodos para trabajar con el "registro" de servidores COM.


"Registro" de servidores COM (3)

La importancia del registro puede subestimarse, pero probablemente sea el pilar principal de COM. Microsoft escribe en el registro, crea una estructura compleja para describir las interfaces y sus atributos (idl), fui un poco diferente.


En la implementación, el registro se basa en el sistema de archivos.
¿Qué tipo de bollos? La comprensibilidad, la simplicidad, la posibilidad de recuperación, un bollo especial al registrar un servidor, puede establecer algún tipo de espacio de nombres (un directorio relativo al registro base en el que se registrarán los objetos del servidor), por lo que puede implementar la integridad y el control de versiones de las aplicaciones utilizando la tecnología.


De las deficiencias, posibles problemas de seguridad, la sustitución de implementaciones de objetos.


Cómo usar, aplicación de muestra (4)

Para que todo funcione, necesitará una pequeña "biblioteca" y un pequeño "programa".


"Biblioteca" no es más que un contenedor que implementa y recopila todo en un solo conjunto, trabajando con el registro, cargando / descargando SO, creando objetos.
Es el único que debe especificarse al compilar la aplicación. Todo lo demás, "Quiero creer", lo hará ella misma.


" Programka " : regsrv es en realidad un análogo del programa RegSrv32 de Microsoft que realiza las mismas acciones (+ la capacidad de especificar un espacio de nombres, + la capacidad de obtener una lista de servidores clsuid y COM registrados).



muestra

 #include "../include/dom.h" #include "../../skel/ihello.h" int main() { Dom::Interface<Dom::IUnknown> unkwn; Dom::Interface<IHello> hello; if (Dom::CreateInstance(Dom::clsid("SimpleHello"), unkwn)) { unkwn->QueryInterface(IHello::guid(), hello); hello->Print(); } else { printf("[WARNING] Class `SimpleHello` not register.\nFirst execute command\n\tregsrv <fullpath>/libskel.so\n... and try again."); } return 0; } 

Dom (5)

Dom (Modelo Dinámico de Objetos), mi implementación para Linux.

git clone


Gracias

Source: https://habr.com/ru/post/es427919/


All Articles