插件技术与应用程序扩展
插件接口标准与设计思路
支持插件的应用程序通常将插件文件集中存放于特定目录下。程序运行时会首先搜索该目录,发现插件后将其加载到应用程序中,程序终止时负责释放插件资源。插件的形式可以是独立可执行文件、动态链接库或其他文件格式,以便于灵活使用。本文以动态链接库形式实现插件管理,因其支持灵活的插件装载和调用。
主程序无法事先预知插件的数量及功能,因此需要与插件之间建立统一的接口标准。插件与主程序通过标准的DLL导出函数交互,主要用于插件对象的创建。以下是常见的接口定义:
BOOL Plug_CreateObject(void ** pobj) 插件类通过派生自基类CPlugBase来实现主要功能,如获取插件图标、功能接口调用和资源释放等。基类CPlugBase的结构如下:
class CPlugBase public: CPlugBase(){}; virtual HICON GetIcon() = 0; virtual void Interface(int k) = 0; virtual void Release() = 0; 为了方便管理插件对象,主程序使用模板类CArray存储插件信息。插件管理结构PLUG_ST记录插件类指针和动态链接库句柄,其定义如下:
typedef struct PLUG_ST, *LPPLUG_ST { CPlugBase * pObj; HINSTANCE hIns; } 插件图标在工具条按钮上显示,主程序通过插件的GetIcon()函数获取图标句柄绘制按钮。插件按钮的命令响应使用ON_COMMAND_RANGE宏实现,覆盖ID范围内的命令。
普通应用程序插件支持
任何普通应用程序都可以通过编码实现插件支持。插件搜索逻辑通常位于主框架类中,首先检查PLUGINS子目录下是否存在插件动态链接库。如果存在则加载插件并添加到插件管理数组中。插件装载过程如下:
PLUG_ST stPs; ZeroMemory(&stPs, sizeof(stPs)); stPs.hIns = LoadLibrary(szPlug); PFN_Plug_CreateObject pFunc = GetProcAddress(stPs.hIns, _T("Plug_CreateObject")); if (pFunc((void **)&stPs.pObj)) m_arrPlugObj.Add(stPs);
插件图标提取并绘制到工具条按钮,按钮资源ID从ID_PLUG_POINTER开始递增。插件按钮的命令响应函数使用ON_COMMAND_RANGE实现,具体处理如下:
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) ON_COMMAND_RANGE(ID_PLUG_POINTER, ID_PLUG_POINTER+256, OnPlugHit)
void CMainFrame::OnPlugHit(UINT nID) { int id = nID - ID_PLUG_POINTER; if (id >= 0 && id < m_arrPlugObj.GetSize()) { if (m_arrPlugObj[id].pObj) m_arrPlugObj[id].pObj->Interface(id);
插件资源释放
在程序终止前,确保释放所有插件资源。插件对象释放包括调用Release()方法和释放动态链接库句柄,最后清空插件管理数组:
for (int i = 0; i < m_arrPlugObj.GetSize(); i++) { if (m_arrPlugObj[i].pObj) m_arrPlugObj[i].pObj->Release();
if (m_arrPlugObj[i].hIns) FreeLibrary(m_arrPlugObj[i].hIns);
m_arrPlugObj.RemoveAll();
插件制作
插件制作涉及创建动态链接库,需注意以下几点:首先确保插件图标在资源中正确引入,并设置为“Use MFC in a Static Library”以便在编译时将资源打包到插件模块。其次,插件必须遵循主程序的接口标准,主要通过导出函数实现。常见的导出函数包括:
LIBRARY "PlugA" EXPORTS Plug_CreateObject @1
导出函数Plug_CreateObject负责创建插件对象:
BOOL WINAPI Plug_CreateObject(void ** pobj) { *pobj = new CPlugA; return *pobj != NULL; 插件类CPlugA继承自CPlugBase,实现插件特定功能,如图标获取和功能接口调用。