DexterChen

大梦谁先觉?凭生我自知,草堂春睡足,窗外日迟迟

 

基于普通DLL的插件模式

基于普通 DLL 的插件模式

 

插件模式已经在软件开发中得到了广泛的应用,这种设计扩展性强,便于插件和主程序独立升级,本文描述了基于普通 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();

      

 

 

posted on 2007-02-25 12:01 DexterChen 阅读(3195) 评论(0)  编辑 收藏 引用 所属分类: C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

搜索

最新评论

阅读排行榜

评论排行榜