嗯,我认为Chris Becke 的建议很好。我不会使用Roger 的第一个解决方案,它仅在名称上使用接口,并且正如他所提到的,可能会遇到抽象类和虚拟方法的编译器处理不兼容的问题。Roger 在他的后续文章中指出了有吸引力的 COM 一致案例。
痛点:你需要学会发出COM接口请求并正确处理IUnknown,至少依赖IUnknown:AddRef和IUnknown:Release。如果接口的实现可以支持多个接口,或者如果方法也可以返回接口,您可能还需要熟悉 IUnknown:QueryInterface。
这是关键的想法。所有使用接口实现(但不实现)的程序都使用一个通用的#include "*.h" 文件,该文件将接口定义为结构 (C) 或 C/C++ 类 (VC++) 或结构(非 VC++,但 C++)。*.h 文件会根据您是在编译 C 语言程序还是 C++ 语言程序而自动进行适当调整。您不必仅仅为了使用 *.h 文件而了解该部分。*.h 文件所做的是定义接口结构或类型,比方说,IFoo,及其虚拟成员函数(并且只有函数,在这种方法中对数据成员没有直接可见性)。
头文件被构造为以一种适用于 C 和适用于 C++ 的方式遵守 COM 二进制标准,而与使用的 C++ 编译器无关。(Java JNI 人想出了这一点。)这意味着它可以在任何来源的单独编译的模块之间工作,只要一个完全由函数入口指针(一个 vtable)组成的结构被所有它们都映射到相同的内存(例如,它们必须全部为 x86 32 位,或全部为 x64)。
在通过某种包装类实现 COM 接口的 DLL 中,您只需要一个工厂入口点。像一个
extern "C" HRESULT MkIFooImplementation(void **ppv);
它返回一个 HRESULT(你也需要了解这些),并且还会在你提供的用于接收 IFoo 接口指针的位置返回一个 *pv。(我正在略读,这里需要您需要更仔细的细节。不要相信我的语法)您为此使用的实际函数原型也在 *.h 文件中声明。
关键是工厂入口,它始终是一个未修饰的 extern "C",它完成了所有必要的包装类创建,然后将 Ifoo 接口指针传递到您指定的位置。这意味着用于创建类的所有内存管理以及用于完成它的所有内存管理等都将在您构建包装器的 DLL 中进行。这是您必须处理这些细节的唯一地方。
当您从工厂函数中获得 OK 结果时,您已经获得了一个接口指针,并且它已经为您保留(已经为您提供的接口指针执行了一个隐式 IFoo:Addref 操作)。
完成接口后,通过调用接口的 IFoo:Release 方法释放它。它是最终版本实现(如果您制作了更多 AddRef'd 副本),它将拆除工厂 DLL 中的类及其接口支持。无论包含工厂函数的 DLL 是否使用与调用代码相同的库,这都是让您正确依赖接口后一致的动态存储分配和释放的原因。
您可能也应该实现 IUnknown:QueryInterface(作为方法 IFoo:QueryInterface),即使它总是失败。如果您想在使用 COM 二进制接口模型时更加复杂,因为您有更多经验,您可以学习提供完整的 QueryInterface 实现。
这可能是太多的信息,但我想指出,你面临的关于 DLL 的异构实现的很多问题都在 COM 二进制接口的定义中得到解决,即使你不需要所有这些,它提供可行的解决方案这一事实很有价值。根据我的经验,一旦你掌握了这一点,你将永远不会忘记它在 C++ 和 C++ 互操作情况下的强大功能。
I haven't sketched the resources you might need to consult for examples and what you have to learn in order to make *.h files and to actually implement factory-function wrappers of the libraries you want to share. If you want to dig deeper, holler.