您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 酒店餐饮 > 基于MFC的插件系统开发
基于MFC的插件系统开发解剖MFC程序现代工业的特征之一就是分工,一旦能够分工,就会出现“术业有专攻”的局面。建筑业需要钢材,冶金需要矿石、采矿需要设备,……,由此带来生机勃勃的现代文明社会。我们很难想象:建造一座楼房的时候,需要的钢材自己炼、需要的砖瓦自己烧、需要的电梯自己造,会是一种什么感觉?MFC编程可能就是这样,如果MFC程序是一座楼,今天的MFC程序员必须亲历亲为,需要亲自盖起楼的主体结构、形成层次、……、装修房间、完成布线等等一系列工作。因此,我们有必要对现在的MFC程序进行一次外科解剖手术,使MFC程序的构造能够实现分工。我们从剖析一个典型的MFC多文档程序开始,典型的MFC程序,通常由一个文档框架主窗口(CMainFrame)以及一组“文档”模板构成。程序的形态取决于CMainFrame,内容取决于其包含的文档模板。在代码结构上,主窗口、文档、文档框架窗口(CChildFrame)、View等类型对象耦合在一起形成了一个通常意义下的MFC程序,对象耦合的过程,被MFC框架巧妙地封装了。因此,多少年来绝大多数场合下人们看到的是一个近乎“永恒”的代码结构:CMultiDocTemplate*pDocTemplate;pDocTemplate=newCMultiDocTemplate(IDR_MsdnPluginSamplTYPE1,RUNTIME_CLASS(CSampleDoc1),RUNTIME_CLASS(CChildFrame1),RUNTIME_CLASS(CUserCtrlView));if(!pDocTemplate)returnFALSE;AddDocTemplate(pDocTemplate);pDocTemplate=newCMultiDocTemplate(IDR_MsdnPluginSamplTYPE2,RUNTIME_CLASS(CSampleDoc2),RUNTIME_CLASS(CChildFrame2),RUNTIME_CLASS(CUserCtrlView));if(!pDocTemplate)returnFALSE;AddDocTemplate(pDocTemplate);...pDocTemplate=newCMultiDocTemplate(IDR_MsdnPluginSamplTYPEn,RUNTIME_CLASS(CSampleDocn),RUNTIME_CLASS(CChildFramen),RUNTIME_CLASS(CUserCtrlView));if(!pDocTemplate)returnFALSE;AddDocTemplate(pDocTemplate);CMainFrame*pMainFrame=newCMainFrame;if(!pMainFrame||!pMainFrame-LoadFrame(IDR_MAINFRAME))returnFALSE;m_pMainWnd=pMainFrame;pMainFrame-ShowWindow(m_nCmdShow);pMainFrame-UpdateWindow();只要你用过MFC,你一定接触过上述代码,从中你会感受到一张沧桑的面孔。这段代码贴切地显示出MFC框架的呆板、僵化。深入了解了这些代码背后发生的故事,你会发现解开MFC框架臃肿、僵化的玄机就在这里。从上面给出的代码中可以看出,MFC程序在初始化阶段通过AddDocTemplate(pDocTemplate);填充了一个文档模板队列。完成文档队列填充后的工作就是实例化一个主窗口,然后创建该窗口并显示出来。这个初始化过程许多年来一直在以相同的模式重复着,就像一个物理规律,几乎时时刻刻地发生在MFC的世界里。每增加一个文档模板,就需要在程序中增加一个文档类、一个文档框架窗口和一个或多个视图类,然后在程序初始化阶段重新构造一个文档模板类,将其填充到文档模板队列中……你的程序至少需要增加3个类。如果要构造支持5个文档类型的MFC程序,得到的程序结构将十分丰满,因为保守估计该程序也得包含15个以上的C++类。对初学者而言,会因此而极大地增强信心,因为他终于写出很大的C++程序了;然而对一个大型的综合程序而言却是一个噩梦,一个系统如果要求100个用户视图、20个文档类型,用MFC框架开发就是件十分“恐怖”的事情。由此我们看到“文档模板队列”是基于文档的MFC程序结构臃肿之症结所在。然而,换个角度看基于文档的MFC程序的结构就很简单:无外乎一个文档模板队列,以及一个支撑文档显示的主框架窗口,许许多多其它对象均属于“亚”层次的二级结构元部件。结构臃肿的症结既然已经暴露,化解的方案也就呼之欲出了。从程序初始化的过程中可以看到,文档模板队列的填充,是通过调用AddDocTemplate完成的,这个调用的参数的数据类型是CMultiDocTemplate*。因此,一个文档模板并非一定是程序内部提供的,也并非必须在程序初始化过程中填充。认识到这一点极为重要!即使在同一个程序内部,不同文档模板之间的关联性一般也很弱,这一点表明,文档模板完全可以从程序结构中剥离。实现文档模板与应用程序结构剥离,意味着实现功能与程序主体的分离,即:应用的内容可以单独开发,根据需要加载。剥离机制面临的技术问题是:是否存在一个可接受的途径使得外部构造的模板可以根据需要插入到一个特定程序的模板队列中。由此可见,调整文档模板队列的填充机制,是化解MFC工程臃肿的良好策略。此外,MFC程序的主线程窗口的匹配,也是在初始化过程中通过:CMainFrame*pMainFrame=newCMainFrame;if(!pMainFrame||!pMainFrame-LoadFrame(IDR_MAINFRAME))returnFALSE;m_pMainWnd=pMainFrame;完成的,m_pMainWnd是个CWnd*类型的指针,即:程序的主线程仅仅需要一个同线程窗口对象的指针,因此,主窗口对象也不一定由程序主体内部提供。主窗口的创建,同样可以从程序中剥离出来,认识到这一点同样重要!对于一个基于文档的MFC程序来说,主窗口的主要责任只是为一系列文档模板形成的体系提供可供显示的框框,我们完全没有必要将程序和一个主窗口死死的焊接在一起,更没必要将一组固定的模板固化在一个特定的程序之中。我们看到:如果实现文档模板从程序中剥离出来,那么全部剥离出来文档模板将形成一个“社会”;如果能够将程序的主窗口也剥离出来,那么剥离出来的主窗口将形成文档社会中的一个个的建筑或组织形态。而这时候,所谓的应用,就是根据特定的需求组织文档模板形成解决方案的一种策略。此时主窗口是特定场合下组织模式的体现,而“文档模板”就是组织成员。因此,我们需要挖掘出一种灵活的文档组织策略,以使得现在的MFC程序员摆脱僵化、臃肿的开发模式。构造最小的MFC程序现在,我们就开始一个精简MFC程序的过程吧。我们记如下类型代码段为(a):CMultiDocTemplate*pDocTemplate;pDocTemplate=newCMultiDocTemplate(IDR_MsdnPluginSamplTYPE,RUNTIME_CLASS(CMsdnPluginSampleDoc),RUNTIME_CLASS(CChildFrame),RUNTIME_CLASS(CUserCtrlView));if(!pDocTemplate)returnFALSE;AddDocTemplate(pDocTemplate);而将如下类型代码段记为(b):CMainFrame*pMainFrame=newCMainFrame;if(!pMainFrame||!pMainFrame-LoadFrame(IDR_MAINFRAME))returnFALSE;m_pMainWnd=pMainFrame;pMainFrame-ShowWindow(m_nCmdShow);我们的思路是,首先在常规MFC多文档程序中删除上述与主窗口和文档相关的创建代码,使原生应用程序空无一物。然后,创建新的工程,在其中完成主窗口和文档的创建工作;最后一步自然是把它们装回去。先进行第一步,我们按照常规的步骤生成一个标准的MFC多文档程序,将(a)、(b)对应的代码删除掉,再删除所有与(a)、(b)相关的所有文件(注意保留应用程序对象对应的文件),并适当调整使这个程序能够正确编译。经过以上处理,将得到一个最小的MFC程序,这个程序中除了一个应用程序对象和一个CAboutDlg之外,什么都没有。因此,运行时什么都不会发生。现在,一个有意思的问题出现了:如何寻找一个合适的途径,使得(a)、(b)中的代码合理地“回归”到我们刚刚得到的最小的MFC程序中?我们回顾一下:(a)的目的仅仅是要填充一个队列。如果忽略具体的软件需求,只要是一个有效的CMultiDocTemplate*指针,只要这个指针能够顺利地传递到(a),填充就能顺利进行,并且填充过程不具备排斥性。这意味着什么?这意味着如果能找到合适的办法,你就可以填充任意多数量的文档模板!(b)的目的是实例化一个窗口对象,创建、加载并匹配给MFC的主线程,但并不局限于规定的窗口类型。虽然这个过程对每个程序的运行时只有一次,正像一个人或者一个公司可以根据意愿选择自己的住所或办公地一样,如果找到合适的办法,一个程序完全可以根据场合匹配适当的主窗口。为了实现(a)、(b)回归到原来的程序,我们需要在原始程序的初始化工程中嵌入两个“回调机制”:一个用于加载(a),另一个用于加载(b)。现在,可以形成策略了:我们需要两类“对象”,一类用来解决(a),另一类用来解决(b)。同时,我们还希望这两类对象能够独立于程序存在,需要的时候能够在程序的合适位置创建即可。从纯粹MFC的角度解决所提到的两类对象是完全可能的,但这样会带来很大的局限,也十分可惜。因此,结合现代的软件技术,我们应该从更广泛的意义上考虑这个问题,由此挖掘出MFC框架与现在主流软件技术的强有力的结合点。我们着重考虑基于COM、.NET、Java等框架考虑实现这两种对象的可能性;本文中,我们工作的基础是.NET。动态加载原理动态加载,是个应用很广泛的程序技巧,COM、.NET、Java均存在软件动态加载的机制。对任何一个.NET程序,CLR提供两种途径检索可动态加载的对象——一个途径是全局的,这类对象必须在全局对象缓冲区中注册;另一个途径是局部的,通过程序的配置文件指定。.NET框架为每个程序提供一个配置文件,这个文件的名字是:“程序名.exe.config”,这个文件实际上就是个标准的XML文件。一个典型的配置文件如下:?xmlversion=1.0encoding=UTF-8?configurationruntimeassemblyBindingxmlns=urn:schemas-microsoft-com:asm.v1probingprivatePath=bin;usercontrol;component//assemblyBinding/runtime/configurationCLR通过probingprivatePath=bin;usercontrol;component/中指定的信息发现当前程序的局部组件路径。可以给privatePath重新赋值以指定新的局部组件检索路径,不同的路径用‘;’分隔。也就是说,除了全局对象缓冲区中注册的对象外,只有程序所在目录以及子目录bin、usercontrol、component中的动态链接库中包含的局部.NET对象才能够被该程序加载。具体的加载实现如下:Assembly*m_pDotNetAppAssembly=Assembly::Load(SLibName);Type*m_pDotNetAppType=m_pDotNetAppAssembly-GetType(SObjectID,true,true);Meth
本文标题:基于MFC的插件系统开发
链接地址:https://www.777doc.com/doc-2570873 .html