转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
动态加载和动态注册类技术的深入探索         ★★★★

动态加载和动态注册类技术的深入探索

作者:闵涛 文章来源:闵涛的学习笔记 点击数:2143 更新时间:2009/4/23 18:44:50
Delphi的包是Delphi IDE的核心技术,没有包也就没有了Delphi的可视化编程。包也可以用在我们开发的项目中,其好处是可以代码共享,减小工程尺寸,单纯通过替换包文件就能实现工程的升级和补丁。但是我们要加载包,就要知道包中已经存在的类。关于如何动态加载包的资料比比皆是我就不想就此问题讨论了。但是Delphi的IDE很是特殊,它无需事先知道你的包有哪些类就能注册组建,创建组建。但是Borland没有公开BPL文件的格式。我们自己是否可以实现IDE的功能呢?
首先我们知道。一个组件包想要能在IDE中使用就要进行注册也就是要创建一个过程例如:
Procedure Register;
Begin
   RegisterComponents(IDE中的页面, [组件类]);
End;
在IDE加载时就要调用这个过程进行注册。
其次我们通过Borland的文档又知道BPL只是一种特殊格式的DLL文件。那么既然IDE可以调用得到注册过程那么注册过程一定要是导出类型(exports)的才行。既然如此我们可以想办法弄明白。写一个包文件。里面包含Test、和TestBtn两个单元。两个单元分别都有注册过程,然后编译成BPL文件。好了我们可以用EXESCOPE这个工具来弄清楚其中的奥秘。

我们可以看到一个函数@Test@Register$qqrv。几乎可以肯定这个函数就是BPL把Test单元中的Register导出的注册函数,而那个@Testbtn@Register$qqrv就一定是Testbtn这个单元的注册函数。可以做一个实验来证明我们的想法,在Test单元的Register的函数中加上ShowMessage(‘你好,你调用了注册函数’);
然后在我们来调用一下包中的函数@Test@Register$qqrv,随便写一个工程看看是不是可以调用得到Test单元中的Register过程。
var
  H                 : Integer;
  regproc           : procedure();
begin
  H := 0;
  H := LoadPackage(''''TestPackage.bpl'''');
  try
    if H <> 0 then
    begin
      RegProc := GetProcAddress(H,''''@Test@Register$qqrv'''');//载入包中的函数
      if Assigned(RegProc) then
      begin
        regproc();//调用函数
      end;
    end;
  finally
    if H <> 0 then
    begin
      UnloadPackage(H);
      H := 0;
    end;
  end;
end;
调用的结果,果然调用到了包中Terst单元的Register过程。但是如何得到注册了哪些类呢?注册组件要用RegisterComponents函数。好在VCL体系的源代码是开放的,我们看看RegisterComponents是如何实现的吧。
在Classes单元我们可以看到:
procedure RegisterComponents(const Page: string;
  const ComponentClasses: array of TComponentClass);
begin
  if Assigned(RegisterComponentsProc) then
    RegisterComponentsProc(Page, ComponentClasses)
  else
    raise EComponentError.CreateRes(@SRegisterError);
end;
画线的是一个函数指针,Delphi的IDE就是在这个指针所指的函数里去作具体的工作。我们也可以利用它来实现我们的注册。
procedure MyRegComponentsProc(const Page: string;
  const ComponentClasses: array of TComponentClass);
var
  I                 : Integer;
  IDEInfo           : PIDEInfo;
begin
  for i := 0 to High(ComponentClasses) do
  begin
    RegisterClass(ComponentClasses[I]);
  end;
end;
然后一条语句RegisterComponentsProc:= @MyRegComponentsProc;似乎就解决问题了。
慢着!RegisterComponentsProc是在Classes单元。但是BPL中的Classes单元是在另一个运行时的包VCL.BPL里面。而我们工程所修改的RegisterComponentsProc的指针是编译在我们的工程中,空间是不同的。所以我们的工程一定要编译成带运行时包VCL.BPL的才行。但是这样一来的话我们也就只能载入和我们所用的编译器相同版本编译器编译出来的BPL文件了,也就是说Delphi6只能载入Delphi6或者BCB6编译出来的BPL文件以此类推。
但是还有一个问题没有解决,那就是如何知道一个包中到底有那些各单元呢?可以通过GetPackageInfo过程来获得。
我已经把加载包的过程封装到了一个类中。整个程序的代码如下:

{ *********************************************************************** }
{                                                                         }
{ 动态加载Package的类                                                     }
{                                                                         }
{ wr960204(王锐)2003-2-20                                                 }
{                                                                         }
{ *********************************************************************** }
unit UnitPackageInfo;

interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  PIDEInfo = ^TIDEInfo;
  TIDEInfo = record
    iClass: TComponentClass;
    iPage: string;
  end;
type
  TPackage = class(TObject)
  private
    FPackHandle: THandle;
    FPackageFileName: string;
    FPageInfos: TList;
    FContainsUnit: TStrings;            //单元名
    FRequiresPackage: TStrings;         //需要的的包
    FDcpBpiName: TStrings;              //
    procedure ClearPageInfo;
    procedure LoadPackage;
    function GetIDEInfo(Index: Integer): TIDEInfo;
    function GetIDEInfoCount: Integer;
  public
    constructor Create(const FileName: string); overload;
    constructor Create(const PackageHandle: THandle); overload;
    destructor Destroy; override;
    function RegClassInPackage: Boolean;

    property IDEInfo[Index: Integer]: TIDEInfo read GetIDEInfo;
    property IDEInfoCount: Integer read GetIDEInfoCount;
    property ContainsUnit: TStrings read FContainsUnit;
    property RequiresPackage: TStrings read FRequiresPackage;
    property DcpBpiName: TStrings read FDcpBpiName;
  end;
implementation

var
  CurrentPackage    : TPackage;

procedure RegComponentsProc(const Page: string;
  const ComponentClasses: array of TComponentClass);
var
  I                 : Integer;
  IDEInfo           : PIDEInfo;
begin
  for i := 0 to High(ComponentClasses) do
  begin
    RegisterClass(ComponentClasses[I]);
    new(IDEInfo);
    IDEInfo.iPage := Page;
    IDEInfo.iClass := ComponentClasses[I];
    CurrentPackage.FPageInfos.Add(IDEInfo);
  end;
end;

procedure EveryUnit(const Name: string; NameType: TNameType; Flags: Byte; Param:
  Pointer);
begin
  case NameType of
    ntContainsUnit:
      CurrentPackage.FContainsUnit.Add(Name);
    ntDcpBpiName:
      CurrentPackage.FDcpBpiName.Add(Name);
    ntRequiresPackage:
      CurrentPackage.FRequiresPackage.Add(Name);
  end;
end;
{ TPackage }

constructor TPackage.Create(const FileName: string);
begin
  FPackageFileName := FileName;
  LoadPackage;
end;

procedure TPackage.ClearPageInfo;
var
  I:Integer;
  IDEInfo:PIDEInfo;
begin
  for i:=FPageInfos.Count-1 downto 0 do
  begin
    IDEInfo:=FPageInfos[I];
    Dispose(IDEInfo);
    FPageInfos.Delete(I);
  end;
  FPageInfos.Clear;
end;

constructor TPackage.Create(const PackageHandle: THandle);
begin
  FPackageFileName := GetModuleName(PackageHandle);
  LoadPackage;
end;

destructor TPackage.Destroy;
var
  I                 : Integer;
begin
  FContainsUnit.Free;
  FRequiresPackage.Free;
  FDcpBpiName.Free;
  if FPackHandle <> 0 then
  begin
    UnRegisterModuleClasses(FPackHandle);
    ClearPageInfo;
    FPageInfos.Free;
    UnloadPackage(FPackHandle);
    FPackHandle := 0;
  end;
  inherited Destroy;
end;

function TPackage.GetIDEInfoCount: Integer;
begin
  Result := FPageInfos.Count;
end;

funct

[1] [2] [3]  下一页


没有相关教程
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台