据我所知,COM的一般思想是链接到一些函数,即QueryInterface,AddRef和Release in a common dll(Kernel32.dll),然后允许你访问接口,这些接口只是一个用指针封装的函数指针表应该调用函数指针的对象.这些函数通过IUnknown公开,你必须继承它.
那一切都有用吗?有没有更好的方法来动态链接和加载到面向对象的代码?如何从dll继承工作 – 对基类的每次调用都必须是暴露的成员函数,即private / protected / public是否被忽略?
解决方法
在最简单的层面上,COM通过接口实现.在c中,如果您对纯虚拟或抽象基类的想法感到满意,那么您已经知道如何在c中定义接口
struct IMyInterface { void Method1() =0; void Method2() =0; };
COM运行时提供了许多适用于Windows环境的额外服务,但在单个应用程序中实现“迷你”COM时,并不是真正需要的,因为动态链接到比dlopen,dlsym传统允许的更多OO接口,等等
COM对象在.dll,.so或.dylib文件中实现,具体取决于您的平台.这些文件需要导出至少一个标准化的函数:DllGetClassObject
在您自己的环境中,您可以根据需要对其进行原型设计,但是在窗口上与COM运行时互操作,显然名称和参数需要符合com标准.
基本思想是,传递指向GUID的指针 – 16个字节,唯一地分配给特定对象,并创建(基于GUID)并返回工厂对象的IClassFactory *.
然后,COM运行时使用工厂对象在调用IClassFactory :: CreateInstance方法时创建对象的实例.
所以,到目前为止你有
>导出至少一个符号的动态库,名为“DllGetClassObject”(或其某些变体)
>一个DllGetClassObject方法,它检查传入的GUID以查看是否和正在请求的对象,然后执行“new CSomeObjectClassFactory”
>一个CSomeObjectClassFactory实现,它实现(派生自)IClassFactory,并将CreateInstance方法实现为CSupportedObject的“新”实例.
> CSomeSupportedObject,它实现了从IUnknown派生的自定义或COM定义的接口.这很重要,因为IClassFactory :: CreateInstance传递了一个IID(同样,这次定义了一个接口的16byte唯一id),它需要对象的QueryInterface.
据我所知,你必须继承它.
实际上,COM是由OLE32.dll实现的,它暴露了一个名为CoCreateInstance的“c”api.该应用程序通过CoCreateInstance GUID,它在Windows注册表中查找 – 其数据库为GUID – > “dll的路径”映射. OLE / COM然后加载(dlopen)dll,调用其DllGetClassObject(dlsym)方法,再次传入GUID,假设成功,OLE / COM然后调用CreateInstance并将结果接口返回给app.
那一切都有用吗?有没有更好的方法来动态链接和加载到面向对象的代码?如何从dll继承工作 – 对基类的每次调用都必须是暴露的成员函数,即private / protected / public是否被忽略?
来自dll / so / dylib的c代码的隐式继承通过将类中的每个方法导出为“装饰”符号来工作.方法名称用类和每个参数的类型进行修饰.这与从静态库(.a或.lib文件iirc)导出符号的方式相同.静态或动态库,“私有,受保护等”始终由编译器强制执行,解析头文件,而不是链接器.
我非常精通C和模板元编程,成员函数和使用boost的全局/静态函数.
c类通常只能通过静态链接从dll导出 – 在加载时加载的dll,而不是在运行时通过dlopen加载. COM允许通过确保COM中使用的所有数据类型都是pod类型或纯虚拟接口来动态加载c接口.如果你打破这个规则,通过定义一个尝试传递boost或任何其他类型对象的接口,你将很快进入一种情况,编译器/链接器需要的不仅仅是头文件来弄清楚最新情况和你的精心准备的“com”dll必须静态或隐式链接才能运行.
COM的另一个规则是,永远不会通过动态库边界传递对象的所有权.即永远不会从dll返回接口或数据,并要求应用程序删除它.接口都需要实现IUnknown,或者至少是Release()方法,它允许对象执行删除操作.任何返回的数据类型同样必须具有一个众所周知的解除分配器 – 如果你有一个名为“CreateBlob”的方法的接口,那么应该有一个名为“DeleteBlob”的伙伴方法.