在应用程序中,常常需要设计一种框架来适应需求的不断变化。经常地,在软件发布之后,用户需要增加新的功能,或者不同的用户需要根据各自特定的需求定制功能。为了达到这个目的而无需重写代码或者重做“开发——编译——测试——发布”等一系列任务,我们可以实现一种在不破坏现有代码的条件下可扩充模块的框架。使用插件(plug-in)的框架可以满足这一需要。
那么什么是使用插件的框架呢?简单地说,这种框架能允许软件在启动时查找附加的功能模块并将其与软件整合。许多应用程序,例如Microsoft Office,就使用了类似的技术来允许第三方开发者来对已有的应用程序进行扩展。怎么样来开发使用插件的框架呢?一种非常简单的方法就是使用DLL(动态链接库)来实现插件扩展。当应用程序启动的时候,在预设的目录中查找符合一定规范的DLL文件。查找完成后,应用程序使用约定好的接口调用DLL模块。
框架的生命周期如下所示:
下面给出一个C++中使用DLL作为插件的例子。1, 应用程序初始化。
2, On_Init()函数在指定的目录中查找DLL文件,例如,plug-in目录。
3, 应用程序对每个查找到的DLL调用load()函数。
4, load()调用之后,应用程序保存每个模块的名字,并对每个模块创建引用,这样,模块中的函数就可以在随后被调用。
5, 在应用程序运行的过程中,当用户选择菜单项时,相应的所定义的功能就会被执行。
6, 关闭应用程序时,调用unload()函数,用来释放load()函数中所申请的资源。
为了将问题简化,这里使用Visual Studio DLL向导创建DLL文件。创建并导出一个名为 fnPlug1的函数,参数的空,返回类型为int,如下所示。
#define PLUG1_API __declspec(dllexport)
extern "C" PLUG1_API int fnPlug1(void);
现在,我们来给DLL加入一个功能以便于观察到其正常工作。向你的DLL函数中加入如下代码。
PLUG1_API int fnPlug1(void)
{
return 1234;
}
当然,在实际应用中,你需要加入特定的的功能而不是简单地返回一个数字。
为了使这个DLL文件作为插件整合进应用程序中,我们需要创建程序来进行驱动。我们的目的是找到所有的DLL文件,对其调用LoadLibrary()函数,存储HMODULE以供以后引用。下面给出一个例子。(注意我们使用.PLX扩展名代替了.DLL)
void CPluginDriverDlg::OnLoad()
{
char filepath[MAX_PATH];
//who are we really? Get the Exe Path
GetModuleFileName(AfxGetApp()->m_hInstance,filepath,MAX_PATH-1);
SetCurrentDirectory(ExtractFilePath(filepath));
CFileFind finder;
CString strWildCard = _T("*.plx"); //look for the plugin files
//call this to set up the finder to iterate through all
//the plugins
BOOL bWorking = finder.FindFile(strWildCard);
while (bWorking)
{
//have to call
//FindNextFile() before GetFileName() or GetFilePath()
//because FindFile just sets the object up and returns
//true if _ANY_ files were found
bWorking = finder.FindNextFile();
HMODULE hm = LoadLibrary(finder.GetFilePath());
if ( !hm )
{
MessageBox("couldn't load");
}
else
{
//loaded OK, so add each library's HMODULE to an array.
//m_dwa is an MFC CDWordArray
m_dwa.Add((DWORD)hm);
}
}
}
接下来,当你想要反复调用你的插件时,使用对每个插件存储的HMODULE来得到函数的地址,接着利用函数地址调用函数。
void CPluginDriverDlg::OnRunPlugins()
{
for(int i=0; i<m_dwa.GetSize() ; i++)
{
//Find a function and use it
PFUNC pFunc = (PFUNC)GetProcAddress(
(HINSTANCE)m_dwa.GetAt(i), _T("fnPlug1"));
if (pFunc != NULL)
{
int n = pFunc();
CString answer ;
answer.Format("The answer is %d", n);
MessageBox(answer);
}
}
}
说明一下,PFUNC的声明形式是:
最后,一定要确保做好清理工作,从内存中释放所有的DLL文件,方法是使用FreeLibrary()函数。我在测试代码的DestroyWindow()中完成了这个功能。typedef int (*PFUNC)(void);
BOOL CPluginDriverDlg::DestroyWindow()
{
for (int i=0; i<m_dwa.GetSize() ; i++)
{
// Free all the libs we used
FreeLibrary((HMODULE)m_dwa[i]);
}
return CDialog::DestroyWindow();
}
实际上就是这么简单。你可以把任意多的插件放到预设的文件夹中;当你准备好后,执行插件中的函数。记住这是一个很简单的例子,它可以很简单地被扩充成健壮的模型,轻松载入你所需要的插件。