【MyBean调试笔记】接口的使用和清理二(关于接口在MyBean中的应用注意)

【DEMO信息】

有问题的DEMO, 由【[北京]火鸟】提交

【问题描述】

程序关闭时出现AV错误

JEGJM3O3N445M38LK{JWTX0

【问题解答】

1:你有一个TDataMoudle的单实例插件,GetBean后他的Owner是vclOwners。在退出程序时清理了VclOwners后,然后清理单实例的接口为nil时出现了AV错误, 因为单实例要保留一份接口, 一般情况是不会出现av的
     这里mybean可以优化下,先清理单实例的接口, 但是也不能保证,如果插件在其他DLL时会出现的情况

2:第二个问题是主窗体上面声明了很多插件的实例接口, 如果不提前Free主窗体, 系统关闭主窗体的时候,Delphi会在清理对象的时候会自动清理接口:   接口:= nil, 这样我们都知道 会触发实例的release方法, 但是这个实例已经被释放了,而那块内存可能被破坏。所以主窗体先释放,避免提前清理接口

     (插件接口如果是由TComponent类已经他的子类实现, 可以在对象的Destroy时手动清理变量Pointer(AComponentIntf) := nil;  这样不会触发release方法。如果是接口管理生命周期的对象请不要这样做)

这也是我越来越讨厌Delphi自动编译add/release方法的原因了, 虽然component的release方法都基本没什么用, 但是如果这个实例对应的内存地址已经被破坏, 再去访问relase对应的那块地址时可能会参数AV

 

出现问题的原理就是我刚刚说的哪两种情况
归根到底还是接口赋值为nil的时候,触发了一块无效的对象空间的relase方法

 

 

【建议使用规范】

1. 窗体除非特殊的情况, 一般不要注册单实例,在关闭后,最好去释放窗体, 比如Action := caFree。

2. 主窗体提前释放,然后清理插件。推荐写法:

image

3. 单实例一般建议用由接口管理对象生命周期插件(继承TInterfacedObject)。而非TCompnent类级子类对象。

[MyBean-插件]MyBean通用报表免费无限制版本发布

 

【优点】

   1.开发时无需安装报表组件(可以直接用编译好的文件,注意版权说明,请自行编译一次相应的报表插件文件)。

   2.无带包烦恼所有版本Delphi都可以使用,不拖累Delphi版本的升级。

   3.可以实现单据的多种样式设计, 报表文件可以远程文件存储(网络共享)和本地文件存储。

   4.可以让每个用户设计自己的报表和设定自己的默认报表。

   5.支持多种报表组件(RM, FR, .. 可以扩展)

   6.调用简单,关联文件少。

 

【界面】

 

【关于版权】
  FastReport,RM的版权归原组件作者所有。请自行编译相关的报表插件,本DEMO中保留的FastReporter.dll,RMReporter.dll文件只能供学习使用,请不要用于商业用途。

【关于该插件
  该插件可以免费无限制使用,如果你觉得好用可以联系作者进行购买源码版本。也可以联系作者进行OEM。
  官方网站: www.diocp.org

 

【关于DEMO说明】
  ReporterDEMO.dpr包含三个文件
  Repoter-Common\MybeanReporterIntf.pas       MyBean报表插件的接口文件。
  Repoter-Common\MybeanReporterObjects.pas    MyBean报表插件的调用是需要使用到的一些基础类。
  Forms\ufrmPluginForm.pas                    演示窗体:如何调用使用插件直接预览,设计,打印。
 
  MyBeanSource                                是MyBean的源文件,编译DEMO需要用到MyBean核心文件,你可以设置工程的library路径,或者加入Delphi的Library Paths中
  编译出来的EXE请放置于BIN\文件夹中

【调用示例代码】
  // 报表对象
  FMyBeanReporter := TMyBeanReporter.Create();
  // 提供一个报表中使用的数据集
  FMyBeanReporter.ReporterDataProvider.AddDataSet(‘cdsMain’, self.cdsMain);

  // 报表归类ID(不同的ID可以有多个设计报表
  FMyBeanReporter.ReporterCatalogId := ‘1002579’; 
  // 当前用户,每个用户可以设置不同的默认报表
  FMyBeanReporter.UserName := cbbUser.Text;
  // 显示报表控制台(可以进行设计,管理报表)
  FMyBeanReporter.ShowConsole();   // DirectPreview(); 直接预览, // DirectPrint(); 直接打印

 

 

免费版下载地址

http://pan.baidu.com/s/1ntpBtBb

MyBean学习视频发布

本来是星期五的QQ 交流会,但是到场人数太少,而且我的带宽也太差,改为视频录制。

讲解的不好请见谅。

本次分为视频分为两部分。

1_20150417.MyBean插件框架的介绍和MDIConsole
1>简单介绍了MyBean插件框架的使用和核心文件关系
2>讲解MIDConsole DEMO运行,和DLL中制作窗体插件需要注意的一些事项

2_20150417.MyBean插件三层共享访问插件_包合并工具使用_报表插件的设计思想
1>讲解怎么样在MyBean中制作数据库访问共享插件
2>怎么使用附加工具(芒果制作),进行项目中包的合并。
3>讲解MyBean的实际应用,插件的设计思想。

视频下载地址
http://pan.baidu.com/s/1gdmuGBx

mybean官方网站: diocp.wedelphi.com
mybean官方论坛: http://www.wedelphi.com/

【MyBean调试笔记】关于单元的释放顺序

【概述】

DEMO提交人:惠商软件  2508696439

问题描述:MDIConsole, DEMO如果Forms单元引用顺序放在mybean.console.pas文件之后如下图所示时:

image

创建同一EXE内的MDI子插件并显示,在不关闭MDI子窗体的情况下,关闭主EXE时,会造成关闭时产生访问违规错误。

image

【调试过程】

看了问题,能重现,能重新的问题就不算什么问题了,一定可以找到原因的,开启debug dcus后,发现错误停留产生时,停留在Screen.FSaveFocusedXXX这一句。调试发现果然Screen为nil了。

(PEV87O}1LU$O[ESGWQ_R_K

 

【问题分析】

mybean在创建插件TComponent子插件时会传入beanFactory.VclOwners, 这样在程序关闭时,清理类工厂时,会释放他,并清理他的子组件,达到自动清理的插件的目的,因为getBean的插件没有手动关闭,是等待清理beanFactory时进行清理的,清理的时候,Screen已经被清理,所以产生了该错误。

我们看看工程文件的引用顺序

program MDIConsole;

uses
  FastMM4,
  FastMM4Messages,

  mybean.console,
  mybean.core.beanFactory,

  Forms,

上面代码来看是会先清理Forms,然后再清理mybean.console,和mybean.core.beanFactory单元,但是因为mybean.core.beanFactory里面引用了Forms单元,所以清理顺序变成了

1.mybean.core.beanFactory,

2.Forms,

3.mybean.console,

 

mybean.console单元中管理了所由的类工厂插件接口,因为类工厂是通过接口来决定生命周期的,所以在清理mybean.console单元的时候,才会释放最后一个工厂接口的引用,也就是释放vclOwners和他的子插件,这个时候application和screen实例都已经被释放了。所以procedure TCustomForm.BeforeDestruction;中访问Screen才会引发异常。

现在来优化下mybean的核心类。

mybean.core.beanFactory和mybean.console单元中都引用Forms单元,这样不管你顺序怎么调整,都让Forms单元成为在这两个单元后面。然后在mybean.core.beanFactory被清理时,清理工厂接口资源。

使顺序变成这样

1.mybean.core.beanFactory,

2.mybean.console,

3.Forms。

【MyBean调试笔记】接口的使用和清理

【DEMO信息】

有问题的DEMO, 由芒果提交

【问题描述】

双击Tab页面关闭页面和插件的时候出现AV异常

image

【问题调试】

我们在调试的时候出现这个错误时断点停在这里

image

可以看到停留在这个位置@IntfClear,指针清理的位置

image

好我们来调试下这个过程,跟一下是什么时候出现的, 在end 处下个断点后切换到cpu页,我们看到在ret之前调用了一过程,这个地址应该是做一些清理工作后然后返回到调用者地址,

`TDZCGURW[(RGU7B72)5CNY

跟进去后,一会就跳到错误的界面,可以说明在清理时出现的,这个时候我们来看看这段代码

复制代码

procedure TfrmMain.pgcMainMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Index: Integer;
  lvPluginForm: IPluginForm;
  lvTabShtEx:TTabShtEx;
begin
  // 左键点击并且双击
  if (Button = mbLeft) and (ssDouble in Shift) then
  begin
    Index := pgcMain.IndexOfTabAt(X, Y);
    if Index >= 0 then
    begin
      lvTabShtEx := TTabShtEx(pgcMain.Pages[Index]);
      lvPluginForm := lvTabShtEx.PluginForm;
      if Application.MessageBox('确认要关闭画面吗?', '询问', MB_OKCANCEL + MB_ICONQUESTION) = IDCANCEL then
        Exit;
      lvPluginForm.freeObject;
      pgcMain.Pages[Index].Free;
    end;
  end;
end;

复制代码

这段代码中有一个lvPluginForm为接口IPluginForm变量, 过程在退出时会执行lvPluginForm := nil和其他一些资源的清理工作,清理的时候会触发对象的__release方法,但是我们看到这个对象已经释放掉了,然后在进行清理的时候出现了上面看到的访问违规的错误。

【问题解决】

找到出现问题的根本,解决起来就很快了,尽量不要等到过程清理时在去清理你的资源,特别是可能访问不存在的资源。在end;之前 加一句lvPluginForm := nil;这样就好了

分类: [MyBean说明书]

MyBean 框架学习笔记<感谢[青铜]整理的如此细致和系统>

MyBean 框架学习笔记

2014/9/15 by lighttop

目 录

MyBean 框架学习笔记……………………………………………………………………………………….. 1

第一章  MyBean简介………………………………………………………………………………………… 3

第二章 MyBean的安装……………………………………………………………………………………….. 4

第三章 认识MyBean………………………………………………………………………………………….. 5

第四章 访问DLL中的插件………………………………………………………………………………… 13

第五章 MyBean的配置文件……………………………………………………………………………….. 19

第六章 插件生存期管理……………………………………………………………………………………. 19

第七章 提高插件开发效率…………………………………………………………………………………. 19

第一章  MyBean简介

1.1 概述

MyBean是一个用于Delphi应用程序开发的开源、轻量级、可配置插件框架。它通过巧妙的系统架构设计,无需复杂的配置和安装,就可使程序开发人员方便地实现应用程序的模块化开发、管理和发布,极大地提高软件开发效率。并且,MyBean还留给程序开发人员最大的灵活性,没有过多的约束条件,真正做到了简便、灵活、高效的特点。

MyBean具有以下特性:

1、零依赖。绿色框架,编译的应用程序无需依赖其他任何文件就可以享受框架提供的服务;

2、可配置。可以通过配置文件(json格式)设置插件文件(DLL或BPL)及其插件对象(称之为bean)的配置信息;

3、热插拔。插件文件(DLL或BPL)可动态加载和卸载,从而实现运行时更新插件目的;

4、颗粒性。只要实现了IInterface的对象(TObject及其子类)都可以作为插件对象。插件对象(bean)可以在单独的DLL或BPL文件内,也可以在同一个EXE文件内;可以是一个复杂窗体,也可以只是一个简单的对象。

5、生命周期管理。可以把插件对象简单地设置为单实例对象,由框架接管插件的生命周期,自动管理生成和销毁。

6. 完全开源。框架全部开源(支持D7 – XE7),并且遵循BSD协议,可免费用于商业软件。

……

正是由于上述特点,MyBean给Delphi 程序员提供了一个插件式开发大型应用软件的全新框架,必将成为最流行的DELPHI插件框架之一!

1.2 术语约定

Bean 本文档用“bean”表示一个插件对象的概念。一个bean就是一个实现了某个约定的接口,并向框架插件工厂进行了注册的类(及其实例)对象。

Plugin 本文档用“Plugin”表示一个包含了一个或多个“bean”(插件对象)的DLL或BPL文件,是插件的宿主。

Console   本文档用“Console”概念,表示一个可以载入不同的plugin(插件宿主文件),并调用其中的各种bean(插件对象)的主控程序。一般情况下可以简单地理解为一个主程序EXE。

1.3 官方资源

MyBean 由 D10.天地弦(QQ:185511468开发。

官方Blog: http://www.cnblogs.com/DKSoft/

官方网站: diocp.wedelphi.com

讨论QQ群: 205486036 (MyBean轻量级配置框架)

MyBean的源码库: https://git.oschina.net/ymofen/delphi-framework-MyBean

讨论群:

第二章 MyBean的安装

1、下载MyBean源码包。可以到https://git.oschina.net/ymofen/delphi-framework-MyBean下载Zip压缩包,也可以用Git客户端下载。

2、将框架源码压缩包解压到一个目录,如D:\VCL\MyBean\。如果用Git客户端,请将项目源码下载到上述目录(这里以D:\VCL\MyBean\为例)。

3、然后把D:\VCL\MyBean\Source添加到Delphi的libray 搜索路径

image001

image002

这样,就已经安装好了!

第三章 认识MyBean

1初步体验

我们先看一个框架自带的例子,以增加感性认识。打开samples\singleDEMO示例项目。这个示例演示了在一个EXE程序内,使用插件的概念调用两个窗口。其中包括一个主窗体 ufrmMain.pas文件,2 个作为插件的子窗体文件(Child目录),三个接口文件(Interface目录)。

项目结构如下图:

image003

我们先看主窗体

image004

主窗体的主要代码:

procedure TfrmMain.btnSingletonFormClick(Sender: TObject);  //创建一个单实例窗体

begin

  with TMyBeanFactoryTools.getBean(‘singletonDEMO’) as IShowAsNormal do

  begin

showAsNormal;

  end;

end;

procedure TfrmMain.Button1Click(Sender: TObject);  //创建一个Bean窗体

begin

  with TMyBeanFactoryTools.getBean(‘tester’) as IUIForm do

  try

showAsModal;

  finally

UIFormFree;

  end;

end;

注意上面红色代码部分,都有一个getBean方法,带一个字符串参数表示插件名称,调用后返回插件对象,然后通过as 操作,转换为对象支持的接口类型(对接口不熟悉的朋友请参阅相关知识)。上面这个getBean方法是TMyBeanFactoryTools 类的一个类方法。

TMyBeanFactoryTools 类本身定义在mybean.tools.beanFactory单元中,所以主窗体需要在引用列表中加入这个单元。

image005

主窗体调用接口非常简单,通过调用TMyBeanFactoryTools的getBean方法,传入插件名称即可返回插件对象,并访问插件的方法。

这种以“插件”的方式调用子窗体,可以看到主窗口并没有引用(uses)子窗口单元文件。也就是说主窗体与子窗体实现了“解藕”。

那么,getBean方法为什么能通过名字找到插件呢?按照猜测,子窗体应该通过什么方法向框架系统进行了某种形式的“注册登记”,这样主窗体才能查找到。

所以,我们接着看子窗体的实现。我们打开其中的ufrmSingleton单元,它的窗体界面如下:

image006

再查看它的代码:

type

  TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)

    Memo1: TMemo;

  private

    { Private declarations }

  public

    { Public declarations }

    procedure FreeObject; stdcall;

    procedure showAsNormal; stdcall;

  end;

看到现在,我们还没有发现子窗体究竟做了什么“注册登记”的操作。不要急,在代码最后,我们发现了initialization段的代码,这段代码在单元刚载入时初始化。那么这里它做了什么呢?

initialization

  beanFactory.RegisterBean(‘singletonDEMO’, TfrmSingleton);

  beanFactory.configBeanSingleton(‘singletonDEMO’, true);

我们发现有一个 beanFactory 对象(实际是一个方法function beanFactory: TBeanFactory;),顾名思义,应该是一个bean工厂,专门生产bean(这里也就是我们要做的插件)。这个工厂类有一个RegisterBean方法,把插件注册到某个“登记簿”中去。而’singletonDEMO’就是插件登记的名字,TfrmSingleton是插件的类型。这就验证了我们的猜测。

后面的beanFactory.configBeanSingleton(‘singletonDEMO’, true),则是指明这个插件是个单例模式的插件,即只能建立一个对象实例。

那么,这个beanFactory类型又是哪里声明的呢?查看一下,在  mybean.core.beanFactory;单元中。所以这个子窗体的uses列表中也有这个单元的名称。

我们暂且不去探究beanFactory的内部是如何工作的,先只要知道它在单元初始化部分登记了插件的类名称,把它登记到了框架核心内部的一份“登记簿”中去。然后主程序窗体通过TMyBeanFactoryTools.getBean (插件名称) 方法调用,通过查找内部“登记簿”,获得插件的类别,并建立类的实例,转换成约定的接口。这就是框架工作的大致流程了。

image007

当然,作为插件的子窗体,也应该有“与从不同”的自觉。因为它肯定要比普通的窗体对象承担一些额外的功能。当然,作为框架使用者来说,这种“不同”之处当然是越少越好,这样使用myBean框架才会不那么繁琐。

那么,作为myBean插件对象的窗体,与普通窗体的不同处到底在哪里呢?要怎么做才能成为一个插件呢?

看TfrmSingleton 的定义:

TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)

这个窗体,在普通TFORM基础上,实现了两个接口IFreeObject和IShowAsNormal,这两个接口分别定义了FreeObject方法和showAsNormal方法。那么IFreeObject和IShowAsNormal本身定义在哪里呢?

通过查找,我们发现 IFreeObject定义在mybean.core.intf单元中,这是框架提供的核心单元,暂不去管它。

而IShowAsNormal定义在uIFormShow单元中:

  IShowAsNormal = interface(IInterface)

    [‘{4A2274AB-3069-4A57-879F-BA3B3D15097D}’]

    procedure showAsNormal; stdcall;

  end;

(因为引用了这两个接口,所以不要忘了uses mybean.core.intf和uIFormShow单元)

而FreeObject方法和showAsNormal方法的实现都很普通。freeObject就是调用self.Free 把对象自身free掉。而showAsNormal就是一个最普通不过的show()。

再回到主窗口的代码,看它是如何调用这个子窗体的:

  with TMyBeanFactoryTools.getBean(‘singletonDEMO’) as IShowAsNormal do

  begin

showAsNormal;

  end;

我们发现,’singletonDEMO’正是子窗口向框架注册时用的名字,而IShowAsNormal 正是子窗口实现的接口之一。主窗口中也引用了这个接口文件,所以可以通过这个接口调用方法,而不管子窗体究竟是什么类型的对象。

而实际上,myBean并不强制要求子窗口一定要实现某个特定的接口,你完全可以随便设定子窗口要实现的接口。唯一的约定,就是主窗口和子窗口(作为插件)之间都要遵循同一套接口,以便主窗口在通过GetBean获得子窗口对象后,能够转型为约定的接口并调用接口定义的方法。myBean是通过接口实现主窗体与插件之间沟通的。

当然,为了开发的便利,框架约定了一个IFreeObject接口。如果插件实现了这个接口,则它就可以自己管理生存期,而不需要程序员手动去销毁。

在这个示例中,有两个子窗体,其中一个TfrmSingleton实现了IFreeObject接口,另一个TfrmTester则没有实现这个接口,在使用后需要程序员手动释放,见主窗体的代码:

with TMyBeanFactoryTools.getBean(‘tester’) as IUIForm do

  try

    showAsModal;

  finally

    UIFormFree;   //手动释(销毁)插件对象

  end;

这个UIFormFree方法也是IUIForm 接口中定义的,在TfrmTester中实现了这个接口方法:

procedure TfrmTester.UIFormFree;

begin

  self.Free;

end;

上面啰啰嗦嗦分析了这么多,其实总结起来就是以下内容:

主窗体端

① 引用mybean.tools.beanFactory   (定义TMyBeanFactoryTools)

② 调用TMyBeanFactoryTools.getBean方法获取插件对象

插件端:

① 引用:mybean.core.beanFactory (beanFactory类的RegisterBean,configBeanSingleton方法),mybean.core.intf (定义FreeObject接口)

② 2、调用:beanFactory.RegisterBean方法注册插件,beanFactory.configBeanSingleton方法配置配件信息。

2、进一步探索

看完了几个窗体的代码,现在我们再来查看这个项目的源代码,看使用myBean框架还需要做些什么准备工作。

program singleDEMO;

uses

  Forms,

  mybean.core.beanFactory,

  mybean.console,

  ufrmMain in ‘ufrmMain.pas’ {frmMain},

  ufrmTester in ‘Child\ufrmTester.pas’ {frmTester},

  uIUIForm in ‘Interface\uIUIForm.pas’,

  ufrmSingleton in ‘Child\ufrmSingleton.pas’ {frmSingleton},

  uIShow in ‘Interface\uIShow.pas’,

  uIFormShow in ‘Interface\uIFormShow.pas’;

{R *.res}

begin

  Application.Initialize;

  registerFactoryObject(beanFactory, ‘default’);

  Application.MainFormOnTaskbar := True;

  Application.CreateForm(TfrmMain, frmMain);

  Application.Run;

end.

注意上面红色部分代码,首先它引用了  mybean.core.beanFactory和 mybean.console两个核心单元。然后在代码执行部分注册了一个工厂类的实例:registerFactoryObject(beanFactory, ‘default’)。

通过上述单元引用,框架运行所需要的环境就建立了。

v 本章小结:

要使用myBean框架,需要做以下几个步骤:

l 主程序端

1、在主程序项目文件(.dpr文件)中引用 mybean.console (提供插件框架环境);

2、在主程序项目文件(.dpr文件)的begin end 部分,添加applicationContextInitialize命令,初始化框架执行环境,载入必要的插件工厂(如果没有找到配置文件,将自动载入程序所在目录下的DLL插件和plugin子目录下的bpl插件);

3、在需要引用插件的单元文件开头,引用mybean.tools.beanFactory单元,并用TMyBeanFactoryTools.getBean(‘TestDll’) as Ixxxxxx (Ixxxxxx 为插件与主程序共同约定的接口)的形式调用插件。

l 插件端(以DLL为例):

1、建立DLL项目,在项目文件中引用 uses mybean.core.beanFactory单元,以提供注册插件所需的工厂类;

2、在项目文件的begin …. end 段内,以beanFactory.RegisterBean(‘beanIDxxx’,TBeanClassxxx)的方式注册插件。其中TBeanClassxxx可以是DLL中定义的任意类标识符(包括窗体),’beanIDxxx’是自己登记这个类时用的唯一标识符号;

3、上述第1-2步注册插件的过程也可以分散在DLL的各单元的 initialization 段。相关单元需要引用  mybean.core.beanFactory单元;

4、注册的插件要实现与主程序共同约定的接口,以供主程序调用。

3、延伸阅读(可选,不影响对框架的使用)

我们先分析mybean.console单元的作用。既然它在uses后就起了作用,说明它在initialization 段里执行了一些东东,所以我们先去看这里。

Initialization

{建立一个记录运行日志的TSafeLogger类型对象,并保存到__beanLogger全局变量中}

  __beanLogger := TSafeLogger.Create;

  __beanLogger.setAppender(TLogFileAppender.Create(False));

  __beanLogger.start;

{建立一个TKeyMapImpl类型的对象,保存到 __instanceKeyMap}

  __instanceKeyMap := TKeyMapImpl.Create;

  __instanceKeyMapKeyIntf := __instanceKeyMap;

{主程序实例的上下文环境对象}

  __instanceAppContext := TApplicationContext.Create;

{转化成接口}

  __instanceAppContextAppContextIntf := __instanceAppContext;

  mybean.core.intf.appPluginContext := __instanceAppContext;

  mybean.core.intf.applicationKeyMap := __instanceKeyMap;

  appPluginContext.checkInitialize;

上面代码的最后一行,是执行checkInitialize ,我们继续跟踪它到底干了啥:

procedure TApplicationContext.checkInitialize;

var

  lvConfigFiles:String;

begin

  if FFactoryObjectList.Count = 0 then

  begin

    checkReady;

    lvConfigFiles := FINIFile.ReadString(‘main’, ‘beanConfigFiles’, ”);

    if lvConfigFiles <> ” then

    begin

      if FTraceLoadFile then

         __beanLogger.logMessage(‘从配置文件中加载bean配置’, ‘LOAD_TRACE_’);

      if checkInitializeFromConfigFiles(lvConfigFiles) > 0 then

      begin

        if FINIFile.ReadBool(‘main’, ‘loadOnStartup’, False) then

        begin

          //加载DLL文件, 把DLL载入

          checkInitializeFactoryObjects;

        end;

      end else

      begin

        if FTraceLoadFile then

           __beanLogger.logMessage(‘没有加载任何配置文件’, ‘LOAD_TRACE_’);

      end;

    end else

    begin

      if FTraceLoadFile then

        __beanLogger.logMessage(‘直接加载DLL文件’, ‘LOAD_TRACE_’);

      executeLoadLibrary;

    end;

  end;

end;

插件配置文件命名:主程序名+’.config.ini’ 或 app.config.ini 。

如果存在配置文件,则FTraceLoadFile := True,否则FTraceLoadFile :=False;


第四章 制作一个DLL中的插件对象

上一章我们分析的例子中,插件与主程序编译在同一个EXE中,似乎感觉不出“插件”应有的样子和作用。而且有些分析讲得过于深入但又不够透彻,第一次接触MyBean的同学可能不容易理解。那么,在这一章,我们将根据上一章总结的知识点,从头开始制作一个主程序和一个供调用的DLL插件,以加深印象和理解。

1制作主程序

1.1 新建一个VCL Forms Appliction 项目,并保存。比如项目保存为DemoConsole,主窗体单元保存为ufrmMain.pas。

image008

根据上一节总结的关键点,我们先查看DemoConsole项目源代码

image009

加入对mybean.console单元的引用,并调用applicationContextInitialize命令初始化框架。

image010

1.2 在主窗口单元中, 引用mybean.tools.beanFactory单元

image011

1.3 设计窗口,添加一个按钮

image012

从上一节总结可知,点击按钮后,我们应该通过getBean方法获取插件实例,并调用其中的接口方法。我们假设要实现的插件将注册一个 “demoPlugForm”的名字,并实现了一个叫IFormShow的接口,该接口有一个 ShowAsModal的方法。

为此,我们书写以下的点击事件过程:

image013

注意代码中 IFormShow和ShowAsModal 下面都有红色波浪线,说明IDE不认识这几个标识符。这是因为我们还没有定义它们。所以下步我们就要定义这些接口。

2、约定插件要实现的接口

从上一步主程序编写过程中,我们约定了一个叫IFormShow的接口,插件窗口也要有这个接口。所以我们单独建立一个单元,定义这个接口。

新建一个单元文件,并保存为 uIFormShow.pas,按下图输入IFormShow的定义。

image014

注意上图中红框部分的GUID,可以通过按CTRL+ALT+G来生成。

然后,在第一步生成的主窗体单元中,uses 这个uIFormShow。

image015

这样,主程序就算是制作好了。

为了满足好奇心,我们尝试编译并运行它,并点击按钮,出现以下错误消息:

image016

这是很自然的,因为我们还没有实现这个插件嘛。那么下步就开始做插件吧。

3制作DLL插件

3.1新建一个DLL项目,保存为demoPlugDLL。

3.2 新建一个窗体,命名为ChildForm,作为DLL中的插件对象,以供主程序调用。我们保存为uChildForm。

image017

3.3 让插件窗口实现约定的接口

作为插件要有被主程序掌控的自觉。它把自己能够实现的动作或功能,以接口的形式交给主程序备案,这个接口相当于供主程序掌控自己的“使用手册”。

第一步建立的主程序认为它“掌握”的插件会实现IFormShow接口。那么,我们的这个Child窗口只好按照主程序希望的样子来做了。由于IFormShow我们已经定义在uIFormShow.pas文件中,那么ChildForm窗体直接引用它,并继承和实现IFormShow接口。

image018

在TFrmChild定义体内按Ctrl+shift+C,实现showAsModal方法:

image019

注意:是self.ShowModal,而不是slef.ShowAsModal,不要看错了。

3.4 注册插件

作为插件bean,要有主动注册登记的良好品德,做一个开房登记身份证的守法好公民。

还记得注册登记的格式么?

         beanFactory.RegisterBean(‘beanIDxxx’,TBeanClassxxx)。

记得把带XXX的标识符改成实际的名称啊,这里只是样品哦。

记得写在哪里么?

对了,应该是在DLL项目文件里的begin…end中间,或是功能单元的initialization段。(说实话,我真的记不住initialization的拼写方式,所以还是复制一下吧)

下面是我们写好的:

image020

可是红色波浪线怎么又来了?Delphi不认识beanFactory和RegisterBean?好吧,谁家孩子谁家管,先叫上它们的“家长”mybean.core.beanFactory,单元,放在uses列表里。

image021

至此,我们的DLL插件就算完成了。编译吧,生成一个DLL文件。然后把它放到主程序所在目录。由于我们把两个项目放在同一目录下,所以默认编译生成的dll就在它该在的地方了。

注:如果一个DLL中有好多个插件,那么也可以在DLL项目文件中集中注册,首先在项目源文件中,添加对mybean.core.beanFactory单元的引用,这样就引入了mybean“注册官”,允许bean们注册自己的身份信息了。然后在begin .. End中间添加注册的语句。如下图所示:

image022

4、运行调试

打开主程序,点“显示插件”按钮:

image023

这个我们从零开始制造的主程序和插件就算完工了,并且如我们希望的一样运行了。是不是很有成就感?


第五章 MyBean的配置文件

第六章 插件之间的沟通

第七章 提高插件开发效率

[MyBean说明书]-如何制作BPL插件

DEMO位置:

    samples\simpleConsole\Lib-bpl

【步骤】:

1. 首先新建一个BPL工程。

2. 添加一个窗体,实现IPluginForm接口(simpleConsole.exe只能显示IPluginForm插件)

3.在窗体单元的部分注册插件

4.copy到simpleConsole.exe所在目录的plug-ins\下面, 为了避免加载系统的BPL(发布时会放在根目录下面),默认加载的bpl插件路径为plug-ins目录

5.可以运行simpleConsole.exe显示窗体插件。

image

bpl工程

image

bpl中插件注册。

image

[MyBean说明书]-如何进行最简单的DEMO

MyBean是轻量级的、绿色的框架,不需要安装任何的组件和携带任何的其他文件,前

期步骤已经相当精简了,仔细阅读完下面简单的五个步骤,就可以编写基于MyBean的插件:

1.加入Delphi的搜索路径

source\frame-common,source\frame-core<新版本所有代码放在source中,所以只要加入source文件夹到搜索路径即可>

image

2.主控台<EXE>进行初始化

exe工程中引用mybean.console.pas, 就可以完成初始化工作了。

image

3.如何在程序中<EXE,DLL>调用插件, 插件是通过注册的beanID去获取,引用mybean.tools.beanFactory.pas,就可以获取到对应的插件

image

4.如何创建插件宿主DLL和注册插件,DLL中引用mybean.core.beanFactory.pas文件,然后在单元或者DLL加载的时候对该单元中的beanFactory对象进行注册。

image

5.运行,EXE和DLL放到同一目录,就可以运行,下面是调用DLL中的DEMO

image

最后.这样最简单的DEMO编译出来了, 如果需要执行数据库的查询,依赖了diocp_bean.dll插件和服务器进行通信,SERVER是,服务端,自带了一个成语的access数据库。

这只是一个最简单的DEMO,真正使用时推荐使用模块配置的方式,这样可以使用MyBean的更高级的特性。

image