C++ PLug

插件模式已经在软件开发中得到了广泛的应用,这种设计扩展性强,便于插件和主程 序独立升级,本文描述了基于普通 DLL 的插件模式的实现方法,并介绍了本人的一些经验。

1. 原理和特点

基于普通 DLL 实现插件模式的原理:利用 LoadLibrary 打开指定的动态链接库,然后用 GetProcAddress 取得库中指定函数的地址并调用其功能。设计插件时应把功能分类并制定接口,插件 管理器针对接口编程。

举个例子,某类插件负责对数据的读和写,现制定接口:

BOOL read(void *pIn);

BOOL write(void *pOut);

我们在主程序中要调用指定插件的读功能,可使用下面代码(略去对返回值的检 验):

typedef BOOL (*FN_Read)(void *pIn);

FN_Read fnRead = NULL;

HMODULE hDll = LoadLibrary(“plugin\test.dll”);

fnRead = (FN_Read) GetProcAddress(hDll, “read”);

BOOL bRet = fnRead(xxxxx);

使用这种方法实现插件模式,不需要注册表的参与,便于达到软件“绿色”的要求, 但管理器调用具体插件的时候,要指定路径(一般是固定的)。

2. 实现方法

一般实际应用中不会直接采用上面例子中的方法,因为不易扩展,而且复用性也差。 不同的人有自己不同的实现方法,下面我来讲一下我的方法。

l 首先,定义一个“插件成员”基类,用来传递插件 DLL 中特定功能函数(接口)的指针。

//--------------------------------

// 描述 : 插件成员类

//--------------------------------

class CPluginMember

{

public:

void (*fnOnOpenPlugin)(void *pParam);

void (*fnOnClosePlugin)(void *pParam);

CPluginMember()

{

fnOnOpenPlugin = NULL;

fnOnClosePlugin = NULL;

};

};

这个类包括两个函数指针,分别是打开插件和关闭插件时要调用的,默认值为空。

l 然后,自己实现一个插件管理器,负责从 DLL 中获得特定的“插件成员”对象,如果这个过程成功,那么就可以通过该“插件成 员”对象调用插件的功能了。

//-------------------------------------------------------------

//DESC : 打开插件

//

//lpszPluginDLL : 插件文件名

//pParam : 参数指针

//

//RETURN : 成功 TRUE 失败 FALSE

//-------------------------------------------------------------

BOOL CPluginManager::Open(LPCTSTR lpszPluginDLL, void *pParam)

{

// 加载 DLL

m_hDll = LoadLibrary(lpszPluginDLL);

if (!m_hDll)

{

// 加载 DLL 失败

_RPT0(_CRT_WARN, "Cannot Load plugin file.\n");

return FALSE;

}

else

{

FN_GetPlugin fnGetPlugin;

// 取得 GetPlugin 地址

fnGetPlugin =(FN_GetPlugin)GetProcAddress(m_hDll, PLUGIN_INTERFACE);

if (NULL == fnGetPlugin)

{

// 取得 GetPlugin 地址失败

_RPT0(_CRT_WARN, "Cannot retrieve GetPlugin()'s handle.\n");

return FALSE;

}

else

{

try

{

// 取得插件的 ( 函数 ) 成员

m_pPlugin = fnGetPlugin();

if(!m_pPlugin)

{

_RPT0(_CRT_WARN, "Cannot get plugin members.\n");

return FALSE;

}

// 执行初始化操作

if(m_pPlugin->fnOnOpenPlugin)

{

m_pPlugin->fnOnOpenPlugin(pParam);

}

}

catch(...)

{

// 取得插件成员发生错误,如类型不匹配

_RPT0(_CRT_WARN, "Error occured when handle plugin members.\n");

return FALSE;

}

return TRUE;

}

}

};

//-------------------------------------------------------------

//DESC : 关闭插件

//

//pParam : 参数指针

//

//RETURN : 成功 TRUE 失败 FALSE

//-------------------------------------------------------------

BOOL CPluginManager::Close(void *pParam)

{

// 执行关闭前的清除操作

if(!m_pPlugin)

{

_RPT0(_CRT_WARN, "No plugin is open, Ignore closing operation\n");

return FALSE;

}

if(m_pPlugin->fnOnClosePlugin)

{

m_pPlugin->fnOnClosePlugin(pParam);

}

m_pPlugin = NULL;

BOOL bRet = FreeLibrary(m_hDll);

m_hDll = NULL; // 说明插件关闭

return bRet;

};

//-------------------------------------------------------------

//DESC : 判断插件是否处于打开状态

//

//RETURN : 处于打开返回 TRUE ,否则 FALSE

//-------------------------------------------------------------

BOOL CPluginManager::IsOpen()

{

return (m_hDll != NULL);

};

有了这两个类,就可以实现基本的插件模型了,其它的功能(比如插件的搜索、类型 判断)都可以在这个基础上衍生出来。这种方法已经成功应用到一个图像处理组件中,为开发提供了不少便利。

3. 使用方法

在插件 DLL 工程中:

l 继承 CPluginMember 并加入所需接口

class CPluginMemberTest : public CPluginMember

{

public:

void (*fnTest)();

};

l 定义一个“插件成员”的全局变量 g_plgTest ,和一个名为 GetPlugin 的函数(可导出), GetPlugin 任务是把 g_plgTest 返回给主程序中的调用者

CPluginMemberTest g_plgTest;

__declspec(dllexport) CPluginMember* GetPlugin();

CPluginMember* GetPlugin()

{

return (CPluginMember*)&g_plgTest;

};

l 将实现指定功能函数的地址,在 DLL 初始化的时候,保存到 g_plgTest

BOOL CTestDllApp::InitInstance()

{

CWinApp::InitInstance();

g_plgTest.fnOnClosePlugin = NULL;

g_plgTest.fnOnOpenPlugin = NULL;

g_plgTest.fnTest = TestFunc;

return TRUE;

}

在主程序工程中使用下述代码即可调用插件的功能了:

CPluginMember member;

CPluginManager manager;

BOOL bRlt = manager.Open(_T(“PluginFullPath”), NULL);

CPluginMemberTest *plgTest = (CPluginMemberTest*)manager.m_pPlugin;

plgTest->fnTest();

manager.Close();

你可能感兴趣的:(设计模式,编程,C++,c,C#)