看了许久,都没有理解这个架构,索性将之铺陈纸上,但愿能稍微入门,此博文转为转载,不是原创。
X3插件基础模块是X3插件框架的最底层独立模块,用于形成其他插件模块。X3插件框架的设计目标是汇集各种常用的轻量级C++插件通用模块,其插件既能灵活
组合到各种系统,又能单独拆开使用。X3是开发代号,不是版本号。
X3插件基础模块用于开发具有统一接口标准的C++插件模块,使其具有COM组件的多种特点(接口与实现分离,一个实现支持多个接口,引用技术管理,模块独立
编译,透明部署,模块可替换等),同时简单易用,轻量化,这样开发人员就能简单快捷的开发出可重用、易于测试的规范模块。
所有插件都是普通DLL,通过在工程内包含辅助文件自动实现了统一的导出函数,通过该导出函数就能获取到在该插件内所实现的所有接口的信息及对象创建函数
地址;主程序使用插件管理器(PluginManager)加载这些DLL,插件管理器通过这个导出函数将各个类ID、对象创建函数地址统一管理起来,从而在插件管理器的
中介作用下让各个插件能相互使用各种接口函数。
1.2 本模块的特点
采用本模块提供的机制和代码文件来开发插件模块,具有下列主要特点。
a)接口定义简单灵活
采用普通的C++接口,既由纯虚函数组成的结构体,不需要特殊的基类,不需要宏和UUID声明;同时可以使用C++的各种变量类型,不受COM接口哪样的约束。例如
interface Ix_Example
{
virtual void Foo() = 0;
virtual void *GetData(std::vector<int> &items) = 0;
};
3使用方法
a)定义接口
在H文件中定义接口,在一个H文件中可以定义一个活多个接口
#pragma once
interface Ix_Example
{
virtual void Foo() = 0;
virtual void *GetData(std::vector<int> &items) = 0;
};
b)定义类UUID
一个组件类如果要让外接模块能创建对象实例,需要指定组件类的唯一标识信息,一般采用GUID串来标识组件类。由于普通字符串与接口指针都是指针,为了用强类型做区分
采用XCLSID辅助类来封装类UID,定义类UID常量,供创建对象时之间使用该常量。
既可以在单独的H文件中定义类UID常量,也可以和接口定义在同一个H文件中。
const XCLSID CLSID_Example("FC02CC93-63FF-41CB-A2CF-D05D05F44B23");
c)使用接口
使用智能指针类Cx_Interface<接口名>来使用接口,调用其接口函数;如果在传递对象时(例如定义函数参数时)不想包含特定的接口文件时,可疑使用智能指针Cx_Ptr。通过
智能指针类自动管理对象的引用计数和生命周期。
#include <XComPtr>
Cx_Ptr MyFunc1()
{
Cx_Interface<Ix_Example> pIFExample(CLSID_Example);
if(pIFExample)
{
pIFExample->Foo();
}
Cx_Interface<Ix_Example2> pIFExample2(pIFExample);
if(pIFExample2.IsNotNull())
{
pIFExample2->Foo2();
}
MyFunc2(Cx_Ptr(PIFExample2));
return Cx_Ptr(pIFExample2);
}
void MyFunc2(const Cx_Ptr &obj)
{
Cx_Interface<Ix_Example> PIFExample(obj);
if(pIFExample)
{
pIFExample->Foo();
}
}
d) 实现接口
在插件内部按普通类的方式来实现一个或多个接口,具体就是在类定义中包含接口文件,实现类从接口派生并实现其接口函数。
#pragma once
#include <Ix_Example.h>
class Cx_Example : public Ix_Example,public Ix_Example2
{
protected:
Cx_Example();
~Cx_Example();
virtual void Foo();
virtual void Foo2();
}
上面的例子中有两个地方值得留意:一个是构造函数和析构函数是保护类型,标识不允许直接实例化对象,也不允许直接删除销毁对象,通过智能指针类Cx_Interface或Cx_Ptr
来自动实例化和销毁对象;另一个是所实现的Foo() 等接口函数声明为保护类型,标识不允许直接调用该对象实例的函数,当然也可以声明为私有类型,标识不允许派生实现
类调用其函数。
e)在插件内登记实现类
插件中的实现类一般不直接用于实例化对象,是通过智能指针类Cx_Interface或Cx_Ptr来实例化对象,需要在插件内登记该插件中有哪些可供实例化的类,实现类对应的类UID
是否为单实例类。
#include "stdafx.h"
#include <XModuleMacro.h>
#include <XModuleImpl.h>
#include "Cx_Example.h"
#include "Cx_ExampleTool.h"
XBEGIN_DEFINE_MODULE()
XDEFINE_CLASSMAP_ENTRY(CLSID_Example,Cx_Example)
XDEFINE_CLASSMAP_ENTRY_Singleton(CLSID_ExampleTool,Cx_ExampleTool)
XEND_DEFINE_MODULE()
其中包含XModuleImpl.h用于自动实现插件内部机制,插件机制如何实现对插件开发者来说不用关心,宏XDEFINE_CLASSMAP_ENTRY用于登记普通类及其类UUID,可实例化出多个对象,XDEFINE_CLASSMAP_ENTRY_Singleton用于登记单实例类及其类UUID,在进程中属于静态实例
对于以MFC扩展动态库或Win32动态库类型创建插件工程的情况,由于DLL入口函数基本上都相同,为了避免在多个插件中重复出现类似的DLL入口函数代码,可以将上面例子中的XEND_DEFINE_MODULE作为下面两个宏之一
XEND_DEFINE_MODULE_MFXEXTDLL
XEND_DEFINE_MODULE_WIN32DLL
1.3 新建插件工程的说明
对于VC++,支持各种类型的工程,例如可在MFC应用程序、MFC常规动态库、MFC扩展动态库、Win32动态库中实现插件功能,在ActiveX控件、ATL COM控件、Win32控制台程序、MFC应用程序、各种动态库工程中使用插件接口
要开发一个插件,通常可使用两种类型的工程,MFC扩展动态库,Win32动态库。采用MFC扩展动态库相对于MFC常规动态库的好处是不需要频繁的切换MFC模块状态。
不论采用哪种类型的工程来开发插件,除了设置必要的头文件包含路径外,所做的额外改动工作如下:
在StdAfx.h中包含XComPtr.h文件,以便使用智能指针类Cx_Interface
在一个CPP文件(Module.cpp)中包含XModuleMacro.h和XModuleImpl.h文件,使用XBEGIN_DEFINE_MODULE等宏来登记可能的实现类;
对于不需要实现插件接口而只想使用接口的工程,例如ActiveX控件,可以在一个CPP文件中包含XComCreator.h文件,不需要XBEGIN_DEFINE_MODULE等宏和插件实现所需要的几个H文件
1.4 文件清单说明
pkg_Core\Interface 插件框架内核接口目录,所有工程都需要
Ix_Object 基本接口定义,所有工程都需要
XComPtr.h 智能指针类,所有工程都需要
Ix_ObjectFactory.h 供XModuleImpl.h或XComCreator.h使用
pkg_Core\Interface\Module 插件实现时所需要的文件
Ix_Module.h 获取自身插件的信息
XModuleMacro.h 插件中实现类登记所用的宏定义
XModuleImpl.h 插件机制实现文件
pkg_core\Interface\PluginManager 供加载插件使用
XComCreator.h 供不实现插件而仅使用插件接口时包含
Ix_PluginLoader.h 插件加载和卸载
PluginManager 封装插件管理器,插件加载和卸载