Chinaunix首页 | 论坛 | 博客
  • 博客访问: 194871
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2017-08-23 16:48
文章分类

全部博文(90)

文章存档

2015年(1)

2011年(21)

2010年(59)

2009年(9)

我的朋友

分类: C/C++

2009-11-22 14:46:37

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

阅读(1395) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~