Хабрахабр

[Из песочницы] Портирование COM на Linux

Мне нравится технология COM. Но речь пойдет не о технологии, восхвалении или недостатках COM, а опыте переноса и реализации на Linux. Велосипед? Целесообразность? Давайте не будем на этом заострять внимание.
COM-объект (1)

Реализация объекта в основном скрывается в динамически подключаемой библиотеке, называемой COM-сервер(2), для использования публикуются и распространяются интерфейсы. В общем понимании, объект класса, реализующий как минимум один COM-интерфейс.

Выделяется особый интерфейс IUnknown, любой COM-объект обязан реализовывать данный интерфейс. COM-интерфейс, абстрактный класс содержащий только чисто виртуальные функции.

В COM он определяется структурой GUID и вот тут столкнемся с первым недостатком COM. Каждый COM-интерфейс должен содержать некий свой идентификатор. Нам он то же нужен, но в более читаемом и понятном виде (назовем его uiid). GUID непонятен и не читаем ну и все остальное описанное на Wiki.

IUnknown и uiid

#define define_uiid(name) \ inline static const std::string& guid() 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) };
}

В нашем случае, т.к. Помимо идентификатора интерфейса, выделяется и идентификатор класса (clsuid), необходимый для создания объекта. это более менее читаемый идентификатор, который может определять суть, можно пока забыть о их публикации (возможно это не хорошо).

Реализует как минимум один COM-интерфейс — IUnknown (любой COM-интерфейс имеет уникальный идентификатор интерфейса). Резюме
COM-объект, содержит единственный идентификатор класса. Разные реализации COM-объекта могут иметь один и тот же идентификатор класса (пример: release и debug версия).

COM-сервер (2)

Сервер должен экспортировать определенный набор функций: Динамически подключаемой библиотека (для Linux это Shared object — so) реализующая как минимум один COM-объект.

extern "C" bool DllCreateInstance(const uiid& iid, void** ppv)

Создает объект класса по clsuid, увеличивает количество ссылок на so, каждый раз при успешном создании объекта. Вызов IUnknown::AddRef, так же должен увеличивать счетчик ссылок на so, а IUnknown::Release должен уменьшать.

extern "C" bool DllCanUnloadNow()

Если количество ссылок на SO равно 0, то можно выгружать библиотеку.

extern "C" bool DllRegisterServer(IUnknown* unknown)

Регистрирует в “реестре” все clsuid сервера. Вызывается единожды при инсталляции COM-сервера.

extern "C" bool DllUnRegisterServer(IUnknown* unknown)

Удаляет из “реестра” записи о зарегистрированных clsuid сервера. Вызывается единожды при деинсталляции COM-сервера.

Пример SimpleHello, объявляем интерфейс IHello:

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

Реализация интерфейса:


/* 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; }
}

Набор макросов скрывает реализации функций, предоставляя более структурированное объявление и логику.

Dom::Implement<SimpleHello, IHello> — скрывает реализацию методов интерфейса IUnknown, добавляет “сахарок”, при объявлении интерфейсов реализуемых объектом (С++11 и variadic templates):


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

Интерфейс IRegistryServer — определяет набор методов работы с “реестром” COM-серверов.

“Реестр” COM-серверов (3)

Microsoft пишет в системный реестр, создает сложную структуру описания интерфейсов и их атрибутов (idl), я пошел немного по другому пути. Важность реестра можно недооценить, но он является наверное главным столпом COM.

Понятность, простота, возможность восстановления, особая плюшка при регистрации сервера можно задать некого рода namespace (директорию относительно базового реестра в которой будет регистрироваться объекты сервера), тем самым можно реализовать целостность и версионность приложений использующих технологию. В реализации реестр базируется на файловой системе.
Какие плюшки?

Из недостатков, возможные проблемы с безопасностью, подмена реализаций объектов.

Как использовать, пример приложения (4)

Для того чтобы заставить все работать потребуется еще небольшая “библиотечка” и небольшая “программка”.

Все остальное, “хочется верить”, она сделает сама. “Библиотечка” — ни что иное как обертка реализующая и собирающая все в единое целое, работу с реестром, загрузку\выгрузку SO, создание объектов.
Она единственная должна быть указана при сборке приложения.

“Программка” — regsrv — собственно это аналог программы Microsoft RegSrv32, выполняющей те же действия (+ возможность указания namespace, + возможность получения списка зарегистрированных clsuid и COM-серверов).

sample


#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 (Dynamic Object Model), моя реализация для Linux.

git clone

Спасибо.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»