自定义COM实现?

use*_*168 8 c++ com

我想在UNIX类型平台上用C++实现COM的自定义实现,以允许我动态加载和链接面向对象的代码.我认为这将基于POSIX提供的类似功能集来加载和调用dll,即dlopen,dlsym和dlclose.

据我所知,COM的一般思想是你链接到一些函数,即QueryInterface,AddRef和Release in a common dll(Kernel32.dll),然后允许你访问接口,这些接口只是一个用指针封装的函数指针表应该调用函数指针的对象.这些函数通过IUnknown公开,你必须继承它.

那一切都有用吗?有没有更好的方法来动态链接和加载到面向对象的代码?如何从dll继承工作 - 对基类的每次调用都必须是暴露的成员函数,即private/protected/public是否被忽略?

我非常精通C++和模板元编程,并且已经拥有一个完全反映的C++系统,即成员属性,成员函数和使用boost的全局/静态函数.

Igo*_*aka 6

要记住以下几点:

  • COM的强大功能主要来自IDL和midl编译器.它允许使用为您生成的所有C/C++样板定义对象和接口的verry succint.

  • COM注册.在Windows上,类ID(CLSID)记录在注册表中,它们与可执行文件相关联.您必须在UNIX环境中提供类似的功能.

  • 整个IUnknown实现是相当简单的,除了QueryInterface在C中实现时(即没有RTTI).

  • COM的另一个方面是IDispatch - 即后期绑定方法调用和发现(只读反射).

看看XPCOM,因为它是一个多平台的COM环境.这真的是你利用其他技术更好的事情之一.它可以吸引很多时间更好地花在其他地方.


Chr*_*cke 4

我希望在 UNIX 类型平台上用 C++ 实现 COM 的自定义实现,以允许我动态加载和链接面向对象的代码。我认为这将基于 POSIX 提供的一组类似的功能来加载和调用 dll,即 dlopen、dlsym 和 dlclose。

在最简单的层面上,COM 是通过接口实现的。在 C++ 中,如果您熟悉纯虚拟或抽象基类的概念,那么您已经知道如何在 C++ 中定义接口

struct IMyInterface {
  void Method1() =0;
  void Method2() =0;
};
Run Code Online (Sandbox Code Playgroud)

COM 运行时提供了许多适用于 Windows 环境的额外服务,但在单个应用程序中实现“迷你”COM 时并不真正需要,作为动态链接到比 dlopen、dlsym、 ETC。

COM 对象在 .dll、.so 或 .dylib 文件中实现,具体取决于您的平台。这些文件需要导出至少一个标准化的函数:DllGetClassObject

在您自己的环境中,您可以随心所欲地对其进行原型设计,但要与 Windows 上的 COM 运行时进行互操作,显然名称和参数需要符合 com 标准。

基本思想是,这是传递一个指向 GUID 的指针 - 唯一分配给特定对象的 16 个字节,它创建(基于 GUID)并返回工厂对象的 IClassFactory*。

然后,当调用 IClassFactory::CreateInstance 方法时,COM 运行时将使用工厂对象来创建对象的实例。

所以,到目前为止你已经

  • 导出至少一个符号的动态库,名为“DllGetClassObject”(或其某种变体)
  • 一个 DllGetClassObject 方法,用于检查传入的 GUID 以查看是否正在请求对象以及正在请求哪个对象,然后执行“new CSomeObjectClassFactory”
  • 一个 CSomeObjectClassFactory 实现,它实现(派生自)IClassFactory,并为 CSupportedObject 的“新”实例实现 CreateInstance 方法。
  • CSomeSupportedObject 实现派生自 IUnknown 的自定义或 COM 定义的接口。这很重要,因为 IClassFactory::CreateInstance 会传递一个 IID(这次又是一个定义接口的 16 字节唯一 ID),它需要在对象上进行 QueryInterface。

我理解 COM 的总体思路是链接到一个通用 dll (Kernel32.dll) 中的几个函数,即 QueryInterface、AddRef 和 Release,然后允许您访问接口,这些接口只是用指针封装的函数指针表指向应为其调用函数指针的对象。这些函数通过 IUnknown 公开,您必须继承它。

实际上,COM 是由 OLE32.dll 实现的,它公开了一个名为 CoCreateInstance 的“c”api。应用程序向 CoCreateInstance 传递了一个 GUID,它在 Windows 注册表中查找该 GUID - 该注册表具有 GUID 数据库 ->“dll 路径”映射。然后OLE/COM加载(dlopen)dll,调用其DllGetClassObject(dlsym)方法,再次传入GUID,假设成功,OLE/COM然后调用CreateInstance并将结果接口返回给app。

那么这一切是如何运作的呢?有没有更好的方法来动态链接和加载面向对象的代码?从 dll 继承是如何工作的——对基类的每次调用都必须是公开的成员函数,即私有/受保护/公共被简单地忽略?

从 dll/so/dylib 隐式继承 C++ 代码的工作原理是将类中的每个方法导出为“修饰”符号。方法名称用每个参数的类和类型进行修饰。这与从静态库(.a 或 .lib 文件 iirc)导出符号的方式相同。静态或动态库,“私有的、受保护的等”。始终由编译器强制执行,解析头文件,而不是链接器。

我非常精通 C++ 和模板元编程,并且已经拥有一个完全反射的 C++ 系统,即使用 boost 的成员属性、成员函数和全局/静态函数。

C++ 类通常只能从具有静态链接的 dll 导出 - 加载时加载的 dll,而不是在运行时通过 dlopen 导出。COM 允许动态加载 C++ 接口,方法是确保 COM 中使用的所有数据类型要么是 pod 类型,要么是纯虚拟接口。如果您违反此规则,通过定义一个尝试传递 boost 或任何其他类型对象的接口,您将很快陷入编译器/链接器需要的不仅仅是头文件来弄清楚发生了什么以及您的情况。精心准备的“com”dll 必须静态或隐式链接才能发挥作用。

COM 的另一条规则是,永远不要跨动态库边界传递对象的所有权。即永远不会从 dll 返回接口或数据,并要求应用程序将其删除。所有接口都需要实现 IUnknown,或者至少实现一个 Release() 方法,该方法允许对象执行删除 this 操作。同样,任何返回的数据类型都必须有一个众所周知的解除分配器 - 如果您有一个名为“CreateBlob”的方法的接口,那么可能应该有一个名为“DeleteBlob”的伙伴方法。