应对上一章,本文主要是分享一下插件内数据共享以及消息传递的简单的实现方案,欢迎大家砸砖。若有疑问的可以先看下之前的文章:
学习插件架构(一):(C++ 实现简单插件方式)
http://blog.csdn.net/xuddk727/article/details/10076409
学习插件架构(二):(对应一以COM方式实现)
http://blog.csdn.net/xuddk727/article/details/10236375
本文对应资源下载(运行请一定先看txt说明):
http://download.csdn.net/detail/xuddk727/6256395
早在六七年前,先辈们跟我讲插件架构的优点时(实质上是推销某款软件),我一听恍然大悟,哇靠,这不是系统么,你瞧:共享全局对象(系统对象);对每个插件对象具有加载卸载的功能(exe我随便拷贝删除),对定制、开发、测试、销售、工作量的具有无与伦比的优势(exe……);并且我还自作聪明的脑补了一条某个exe崩溃不影响其他exe运行(稳定性);难道他们来推销…?唔,事实证明尽管我想岔了,但是从今天来看系统从某种程度上就是插件架构的终极目标,因为他满足这些特性,我们所学习的exe加载dll这种架构也仅仅因为这是其中一种平台支持的扩展而已。我们仍能见到一些架构方式,他们的下面是一堆exe,即主程序a.exe调用功能实质是一个b.exe,诚然,这种方式比进程内安全因素考虑的要少一些,因为只要设计的不太烂,b.exe崩溃其他功能仍能正常运行,但是,它同样也有缺陷,比如,数据共享远不如进程内的方便就是一条。所以,我们去评判某个架构好坏的时候至少首先得考虑相应的应用场景。
当然,由于上面所述那条原因,笔者犯了难,因为笔者凭空想不出来什么应用场景,自然就无从针对的去设计。好在随后及时幡然悔悟了,我要做的,只是展示简单的数据共享以及通讯方式,至于具体架构,唔,可别所有的都组合起几种来用,否则不仅凌乱,并且使用和维护起来就复杂了,所以还是尽量在保证考虑的完善够用的情况下保持接口简洁。言归正传,通讯方式很多种,常见的自己实现callback接口、连接点、剪贴板等等,甚至如果不考虑跨语言,传递指针也无所谓,只要能实现,本身这些方式没好坏之分。只是在考虑设计以及实现的复杂度、安全性等才能体现出你架构中所设计的通讯方式的好坏。本文简单展示了一种组件内的广播,且让我跟随代码来简单讲解下实现。
仍沿用原来的工程,并稍作修改。首先,我们修改一下PluginObject插件接口.不再是getname和execute,但仍然很简单。
共四个方法:
HRESULT Init([in] IPluginAppMgr*pApplication, [out]LONG* lVersion,[in] LanguagePackagelanguage, [out]BSTR* strCmdName);
这个方法在插件运行时加载所有插件时执行(见注1),主要是用在插件内部初始化的时候。4个参数,分别是传入插件运行时的接口,传出该dll的版本号(注2),传入当前使用的语言包(注2),传出这个模块的名称。
HRESULT UnInit([in] IPluginAppMgr*pApplication);
这个方法在被释放前调用,主要是清理内存等工作。
HRESULT Execute([in] IPluginAppMgr*pApplication);
与以前大致类似,所不同的是可以获得运行时的接口,得到一些全局信息。
HRESULT Mutual([in] IPluginAppMgr*pApplication, [in]IDispatch* pSendDisp,[in] VARIANT_BOOLbWantReturn, [in]BSTR strMsgContian);
这个接口在某个插件发送广播时被调用,本文主要是依他进行简单交互。第二个参数传入发送这个广播命令插件的IDispatch接口,bWantReturn标示他是否需要返回结果,字符串代表了传递的消息。
当我们的插件运行到某个时刻需要交互时,他调用运行时的广播接口即可让运行时对每个插件进行轮询。
调用代码见PluginTestBroadCast工程。
if (message == WM_PROGRESS_SHOW)
{
if (m_pSysApplication)
{
m_pSysApplication->BroadCast(NULL,VARIANT_FALSE,TEXT("Show();"));
}
return 0;
}
IPluginAppMgr内部是这么实现的:
STDMETHODIMP CPluginAppMgr::BroadCast(IDispatch* pDisp,VARIANT_BOOL bWantReturn,BSTR strMsgContian)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
IPluginAppMgr* pApplication= static_cast(this);
UINT nSize = m_vPlugin.size();
for (UINT i = 0 ; i < nSize; i++)
m_vPlugin[i].pDisp->Mutual(pApplication,pDisp,bWantReturn,strMsgContian);
return S_OK;
}
我这边工程做了一个简单示例:
PluginTestBroadCast工程弹出一个对话框,当点击对话框按钮时,他创建了一个线程去发送广播,当广播到某个能解析的插件时,他就会执行相应的操作(示例工程PluginProgress)。我们看下效果:
其中测试广播对话框由PluginTestBroadCast这个工程提供,进度条则是由PluginProgress提供的。我们在PluginProgress里实现了一个简单反射,用来分析文本并做相应操作。
这就完成了一个简单的交互。
友情提示:运行时需要先对插件进行注册,运行环境需要安装大于等于VS2008SP1。另编译MFCPainter这个exe框架工程需要VS2008 SP1。
注1: 当插件运行时枚举并创建了接口之后会对这些接口进行初始化调用插件的Init接口。
注2:版本号以及语言包等将在后续博文中体现用处,本文暂时无用,敬请关注。