插件框架内核的设计

1、应用接口定义

没有基类,没有显式的接口ID,全部由纯虚函数组成,例如:

interface Ix_MyObj
{
    virtual void foo() = 0;
};

 

2、实现接口

接口实现类(也叫组件类):从接口派生,不需要特殊基类,没有显式的组件类ID,例如:

#include "Ix_MyObj.h"

class Cx_MyObj : public Ix_MyObj
{
protected:
    Cx_MyObj();
    virtual ~Cx_MyObj();

    virtual void foo();
};

为了针对接口编程、让使用者不依赖于具体实现类,该类应当不能直接实例化,所以可以将构造函数设置为保护成员。

一个实现类可以实现多个接口,其方法是从多个接口派生,然后实现接口的函数。

实现类可以继承,这样就允许多个类复用相同的实现部分。

 

 

3、组件类ID的形式

要实例化一个组件类,需要知道其组件类ID。通常组件类ID有三种形式:

1)在类定义中使用__declspec(uuid())关键字指定,使用__uuidof引用该组件类ID,该方法在COM中广泛使用;

2)直接使用GUID字符串定义一个常量名,需要用到组件类ID时使用该常量名,不依赖于编译环境;

3)使用DWORD值定义唯一的模块ID,组件类ID在模块ID的基础上进行1到255的偏移,该方法在飞腾创艺产品中使用。

考虑到尽可能通用、编译环境的uuid复杂性,选择了第二种方法。

 

专门在一个H文件中定义可供外部实例化的组件类的ID,这样使用者不需要知道该类如何实现就能使用该组件类ID,例如:

const LPCWSTR ClsID_MyObj        = L"80313e22-597a-4216-a282-b8ed85722c9c";
const LPCWSTR ClsID_OtherObj    = L"d57720ee-f816-4f40-a47b-44ea47b15321";

4、公共接口 Ix_Object

所有接口都可转换到Ix_Object接口,该接口使用引用技术机制管理对象生命周期。

Ix_Object的继承关系如下图所示:

image

interface Ix_Object
{
    virtual void AddRef() = 0;
    virtual void Release() = 0;
};

在模板类Cx_Object中实现了Ix_Object接口,实现了对象创建函数(静态成员)CreateObject。

5、单实例对象

用模板类Cx_SingletonObject标记单实例类,Cx_SingletonObject在第一次创建对象时将记下该对象,下次再创建时直接返回该对象的引用,同时将该对象添加到全局链表中,以便模块卸载时能销毁该对象。

Cx_ModuleItem类负责管理一个模块内的单实例对象,采用了链表技术。其ClearModuleItems函数在插件模块卸载时调用。

6、标记一个模块有哪些可实例化的组件类

在一个模块中标记出有哪些组件类可实例化、组件类对应的类ID、组件类创建类型(普通类、单实例类、支持特殊接口的单实例类等),在一个CPP文件中统一指定这些信息便于管理,避免分散在各个CPP文件中。这些信息记录到一个_XCLASSMETA_ENTRY 静态数组中,数组元素包含组件类ID、类名、创建对象的模板类等信息。

_XCLASSMETA_ENTRY 的 clsid 为组件类ID(如ClsID_MyObj),pfnCreator 为对象创建函数的地址(如&Cx_Object<Cx_MyObj>::CreateObject)。该模块的所有可实例化组件类的信息都放到 _XCLASSMETA_ENTRY::s_classes 数组中。

struct _XCLASSMETA_ENTRY
{
    LPCWSTR                clsid;
    PFNXObjectCreator    pfnCreator;

    static const _XCLASSMETA_ENTRY s_classes[];
};

下面是一个模块的组件类数组信息的例子:

const _XCLASSMETA_ENTRY _XCLASSMETA_ENTRY::s_classes[] = {
    { ClsID_MyObj, &Cx_Object<Cx_MyObj>::CreateObject },
    { L"", NULL }
};

为了方便于使用,利用宏来简化上面的代码,简化后的结果为:

XBEGIN_DEFINE_MODULE()
    XDEFINE_CLASSMAP_ENTRY(ClsID_MyObj, Cx_MyObj)
XEND_DEFINE_MODULE()

需要下列宏:

XBEGIN_DEFINE_MODULE()

    XDEFINE_CLASSMAP_ENTRY(clsid, cls)

    XDEFINE_CLASSMAP_ENTRY_Singleton(clsid, cls)

    XDEFINE_SPECIAL_INTERFACE_ENTRY_Singleton(clsid, iid, cls)

XEND_DEFINE_MODULE()

7、创建对象的原理

使用 xCreateInstance 函数创建对象,创建一个对象的内部过程为:根据 clsid 在 _XCLASSMETA_ENTRY::s_classes 中查找对象创建函数地址,创建出一个对象,得到 Ix_Object 指针,然后动态转换为特定接口。

对于扩越多个插件的对象创建,是由插件管理器来进行的。由插件管理器将所有插件的 clsid 对应的对象创建函数地址管理起来,当在一个插件内部无法创建对象时,进入插件管理器继续创建对象。

使用智能指针类Cx_Interface和Cx_Ptr来进行对象创建、对象生命期管理。

8、插件管理器原理

将所有插件管理起来,使得插件模块之间可以相互使用接口。具体内容和以前版本相似,省略。

原文地址:https://www.cnblogs.com/rhcad/p/1605950.html