如果动态加载调用BPL的话我觉得完全可以用DLL替代,BPL没有优点。大家都来说两句。

解决方案 »

  1.   

    That is correct. A BPL is a DLL. (But not all DLLs are BPLs.) > But I still found some different, such as that I can create a 
    > object from the Host exe and that pass to a BPL and modify it safely, but 
    > if I do same to a dll, I can not modify any referenced property of the object. When you use packages, there is only ever one copy of any unit in 
    memory. One copy of Forms, one copy of SysUtils, one copy of System 
    (well, most of it), one copy of StdCtrls, etc. All class-related operations, such as the "is" and "as" operators, rely 
    on class references. Class references are actually just addresses. They 
    point to definitions for the layouts of the classes' internals. (They 
    point to what's called the virtual-method table, the VMT.) Two classes 
    are the same if they point to the same VMT -- if the addresses are equal. When you have a class defined in the EXE's copy of StdCtrls and the same 
    class defined in a DLL's copy of StdCtrls, those classes will really 
    have different addresses. The "is" and "as" operators won't work with 
    cross-module clases. But when you use packages, there is only one copy 
    of the class, kept in vcl70.bpl, so all modules that reference that 
    package will share a single class definition. Another factor is the memory manager in System. All string allocations 
    ultimately call GetMem and FreeMem. If the EXE allocates a string, it 
    uses its own GetMem. It then passes the string to a DLL, and the DLL 
    might try to free it. The DLL will call its own copy of FreeMem, which 
    won't have access to the EXE's memory-manager structures, and you'll get 
    errors. With packages, everything will use the same memory manager from 
    rtl70.bpl. (This can also be solved by using a shared memory manager 
    between the EXE and the DLL; ShareMem is one example. That won't solve 
    the class-comparison problem, though.) Above, I said the major difference between BPLs and DLLs is the number 
    of exported functions. With a DLL, the only things exported are what 
    appear in the "exports" clause that you write yourself. With a BPL, 
    everything from all the units' "interface" sections gets exported, 
    including global variables and class definitions. Also exported are the 
    addresses of the "initialization" and "finalization" sections. And, 
    internally, that is indeed the major difference. A BPL exports the 
    functions necessary for the RTL to recognize the file as being a BPL and 
    not just a generic DLL. If you call LoadPackage, i will call LoadLibrary 
    to load it like a normal DLL, and then it will call all the package's 
    units' initialization sections and do a fe other housekeeping 
    operations. Calling a package's functions generates the same kind of 
    assembler code as is generated when you call a DLL function. 
      

  2.   

    一般我们编写编译一个DELPHI应用程序时,会产生一个EXE文件,也就是一个独立的WINDOWS应用程序。很重要的一点:区别于Visual Basic,DELPHI产生的是预先包裹的应用程序是不需要大量的运行库(DLL's)。假设:打开Delphi默认的工程(只有一个空白form),F9她将编译生成一个大约295 KB (Delphi 5)的可执行文件。然后打开Project | Options,把‘Build with runtime packages’选上再编译一下,EXE文件大小就只有15 KB左右了。我们编译一个DELPHI应用程序时默认地没有选择'Build with runtime packages',编译器将把程序运行所需要的代码直接写入你的EXE文件中,因此产生的程序是一个相对独立的程序,并不需要任何附属的支持文件(例如动态运行库文件DLL),这也就知道了为什么DELPHI产生的应用程序为什么都那么大。要建立尽可能小的DELPHI程序,方法之一就要充分发挥Borland package libraries的作用,简称BPL。先说什么是包?简而言之,一个包就是一个在DELPHI的IDE环境中被DELPHI应用程序共享的特殊的动态链接库。包允许我们通过多级应用将我们的程序的一部分当做一个分离的模块供其他应用程序来共享。包大致可分为运行期包(Run-time packages)和设计期包(Design-time packages):运行期包-当运行程序时提供VCL和库函数的支持,操作上很类似标准的动态链接库。设计期包-用来在DELPHI的IDE环境安装控件和为控件建立特殊的属性编辑器。设计期包允许包含控件、属性和控件编辑器等等,在IDE环境中,这类包是程序设计所必需的,也仅仅是DELPHI使用,并不和开发的应用程序一起分发。知道这些包的运用,我们也就知道了运行期包是如何做处理和它们对DELPHI程序员有什么帮助了。有一点必须说明:想很好地运用包并不要求你先成为一个成熟的控件编写者。DELPHI编程初学者也可以和应该尝试去接触包的概念,这将有利于你更好地理解包和DELPHI的工作关系。
      

  3.   

    是选择BPL 还是 DLL?区别又在哪里?你可能很奇怪为什么要选择使用运行期包,而不是DLL,或者还有其他什么方法。相对于DLL而言,包裹的概念是DELPHI开发中所特有的,就是说其他语言编写的应用程序不能引用DELPHI建立的包裹。即使包是一种被DELPHI编写的应用程序所使用的动态链接库,它也同时提供给了DELPHI程序员更多的库函数支持。通常我们在DELPHI中建立动态链接库(DLLs)是用来存储不同环境下应用程序所需要使用到的过程和函数,而包不仅能够包含代码单元(untits)、构件和FORMs,还能包含DELPHI中的类(classes)-这就使我们能够在其中引用对象向导编码(object oriented code)。在包裹中,我们可以保存完整的通用DELPHI构件,而动态运行库(DLL's)对此则无能为力了。此外,在缩减程序代码上,DLLs和BPLs扮演着同样重要的角色,其主要原因就是在使用包裹或动态链接库技术后,都直接地减少了程序的文件大小。当然,还要说明的是:执行程序需要加载的DLLs或BPLs也可能会是很庞大的。例如如果需要分发你的包裹文件(主要是VCL包,vcl50.BPL)至少有2MB左右。尽管如此,如果你是要分发共享同个包的多个应用程序,你就可以省很多事了。当用户方系统中已经存在程序运行需要的部分文件(如:标准的DELPHI BPLs)后,就只需要下载程序的最小执行文件了。如果你的程序工程主要是通过INTERNET等方式分发和开展,那效率显然有很大的提高。同时,包的应用也节省系统内存,因为动态链接的结果就是:只有一个VCL被读入内存供所有使用运行期包的DLEPHI应用程序使用。
    包裹的版本问题当你想升级你的动态链接库时(改变其中一些执行函数),你可以简单地编译产生新的程序文件,并上载新版本文件,所有正在使用该动态链接库的应用程序仍将工作(除非你已经将存在的旧版本程序去除)。换个角度来讲,在升级新包裹文件的同时,不要忘记升级程序的执行文件。正如你所了解的,包裹文件就是一个单元文件(units)的集合,所有编译过的单元文件(DCU)都含有版本信息,因此,除非我们有单元文件的源码(source),否则我们不能在DELPHI4或5中使用编译过的单元,所以一旦我们改变了单元文件中接口部分uses 子句中列举出的任一单元文件,该文件就需要重新编译。编译器将检查DCU文件的版本信息,并决定单元是否需要重新编译。因此我们不能在DELPHI5编译的应用程序中使用在DELPHI6下编译的包,任何为你的应用程序服务的包和你的应用程序必须在相同环境下编译。因此,当给包裹命名的时要保留包裹名中包含有DELPHI的版本信息(如'AboutDP50',其中50就代表Delphi 5)。这可以有效防止文件版本的冲突问题,也可以避免很多不必要的麻烦,包使用者可以更清楚包的版本和包裹适用于哪个DELPHI编译器。如果你要分发运行期或设计期包给其他DELPHI程序员,建议同时提供了.DCP(含有包的头信息和各个单元文件)和.BPL文件,还有包中所包含的所有单元文件的.DCU文件。
      

  4.   

    bpl对于局域网来说其实是优于bpl的,更效率些。
    dll更容易被其他语言调用。
      

  5.   

    1 个 exe + 大量 bpl + 少量 dll
      

  6.   

    如果你要传递类的类型信息,就必须用BPL,否则类型不能穿透。BPL还解决了些DLL的其它问题,例如说支持设计期类型共享,程序可以直接引用BPL中的类型。看你怎么架构程序吧,如果是传统的函数+结构体,可能DLL就够了。另外如果你要和其它语言交互,要只能DLL。如果你是面向对象架构,推荐以BPL为主,方便共享类库。
      

  7.   

    还有,BPL本身就是一种特殊的DLL。说DLL有很多优点,BPL没有优点,我觉得有点搞笑。
      

  8.   

    BPL可以共享类定义信息,这个是很无敌的。
      

  9.   

    BPL有优点,也有缺点。
    优点:BPL可以导出类信息。用BPL可以共享类库。
    缺点:不同BPL在引用同一单元时,会使Package引用关系变得复杂。
      

  10.   

    第二问题是设计上的问题,BPL编译一般是严格禁止隐性引入单元的,否则就很容易出现多个BPL用了同一单元的问题。如果各个BPL分别编译一个单元的话,那还怎么共享类型信息呢(跟DLL一样了)?因此必须正确理解BPL,才能用好BPL。
      

  11.   

    这样使用DLL没有问题:  {主程序和ProjDll.dll都带了rtl;vcl包编译}
      LiraryModule := LoadLibrary('bin\ProjDll.dll');
      if LiraryModule <> 0 then
      try
        AClass := GetClass('TForm1'); {ProjDll.dll中注册了TForm1类}
        if AClass <> nil then
        with TFormClass(AClass).Create(Application) do
        try
          ShowModal;
        finally
          Free;
        end;
      finally
        FreeLibrary(LiraryModule);
      end;
      

  12.   

    请注意讨论前提,而且说的是BPL与DLL对比,不是BPL与零对比。
      

  13.   

    支持这个。请看这里:http://hi.baidu.com/0xcea4/blog/item/4305b9cdad5fa61d00e92809.html,基于bpl的插件框架,你可以看到bpl比dll更优的地方
      

  14.   

    如果一定要只比较动态加载,我觉得意义不大。当然,如果BPL是要完全动态加载的话,一般是要拆分成接口和实现包,静态依赖接口包,动态加载实现包。常需要使用FindClass,根据名称来找接口实现。可以做成配置来动态决定加载哪个包里面的实现。可以了解下IOC思想,如spring容器等,其思想的实现也会用到BPL。
      

  15.   

    当然,如果只是针对你这个个例,可以说DLL就够了。
      

  16.   

    前几天做过的试验,bpl 和 dll 差不多。
    delphi 下解耦,请使用“接口”
    unit uMain;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      IMyTest = interface // 主接口
        ['{9A1E99F7-5B77-4E31-991E-46DEAD30B650}']
        procedure Hellow;stdcall;
        function Hello(const a,b:Integer):Integer;stdcall;
        procedure SetBase(const X:Integer);stdcall;
      end;  IMySub = interface // 后来发现需要拓展一些内容
        ['{81936A15-DC1A-49B7-B5DC-791C0C5A35F9}']
        procedure ttt;stdcall;
      end;  TMyTest = class(TInterfacedObject,IMyTest,IMySub) // 接口实现类
      private
        FBase:Integer;
      public
        procedure Hellow;stdcall;
        function Hello(const a,b:Integer):Integer;stdcall;
        procedure SetBase(const X:Integer);stdcall;    procedure ttt;stdcall;
      end;  IMyDLLTest = interface // 这里接口的实现在 dll 中
        ['{D40DF4C9-4CFA-44E7-A1A3-0B2668BF1502}']
        procedure Hello;stdcall;
      end;  TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;
      _Test:procedure (const intf);stdcall;
      _RegApp:procedure (App:TApplication;out intf);stdcall;
    //procedure Test(const intf);stdcall;external 'MyDll.dll';implementation{$R *.dfm}{ TMyTest }function TMyTest.Hello(const a, b: Integer): Integer;
    begin
      Result := a + b + FBase;
    end;procedure TMyTest.Hellow;
    begin
      ShowMessage('Hellow');
    end;procedure TMyTest.SetBase(const X: Integer);
    begin
      FBase := x;
    end;procedure TMyTest.ttt;
    begin
      ShowMessage('ttt');
    end;procedure TForm1.Button1Click(Sender: TObject);
    var intf:IMyTest;
    begin
      // 这里创建实例,给 dll 中用
      intf := TMyTest.Create;
      _Test(intf);
      ShowMessage(IntToStr(intf.Hello(1,2)));
      intf := nil;
    end;procedure TForm1.Button2Click(Sender: TObject);
    var intf:IMyDLLTest;
    begin
      // 从 dll 中找到实现类
      _RegApp(Application,intf);
      if intf <> nil then
        intf.Hello;
    end;procedure TForm1.Button3Click(Sender: TObject);
    var hh:THandle;pp:TProcedure;
    begin
      hh := LoadPackage('MyBPL.bpl');
      if hh <> 0 then
      begin
        @pp := GetProcAddress(hh,'@Ubpl@DuoTest$qqrv');
        if @pp <> nil then
          pp;
      end;
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    var hh:THandle;
    begin
      hh := LoadLibrary('MyDll.dll');
      @_Test := GetProcAddress(hh,'Test');
      @_RegApp := GetProcAddress(hh,'RegApp');end;end.
      

  17.   


    library MyDll;{ Important note about DLL memory management: ShareMem must be the
      first unit in your library's USES clause AND your project's (select
      Project-View Source) USES clause if your DLL exports any procedures or
      functions that pass strings as parameters or function results. This
      applies to all strings passed to and from your DLL--even those that
      are nested in records and classes. ShareMem is the interface unit to
      the BORLNDMM.DLL shared memory manager, which must be deployed along
      with your DLL. To avoid using BORLNDMM.DLL, pass string information
      using PChar or ShortString parameters. }uses
      SysUtils,
      Classes,Dialogs,
      Forms;{$R *.res}type
      IMyTest = interface
        ['{9A1E99F7-5B77-4E31-991E-46DEAD30B650}']
        procedure Hellow;stdcall;
        function Hello(const a,b:Integer):Integer;stdcall;
        procedure SetBase(const X:Integer);stdcall;
      end;  IMySub = interface
        ['{81936A15-DC1A-49B7-B5DC-791C0C5A35F9}']
        procedure ttt;stdcall;
      end;  IMyDLLTest = interface
        ['{D40DF4C9-4CFA-44E7-A1A3-0B2668BF1502}']
        procedure Hello;stdcall;
      end;  TMyDLLTest = class(TInterfacedObject,IMyDLLTest)
        procedure Hello;stdcall;
      end;var
     _App:TApplication;procedure Test(const intf);stdcall;
    var ii:IMySub;
    begin
      IMyTest(intf).SetBase(3);
      IMyTest(intf).Hellow;
      Supports(IMyTest(intf),IMySub,ii);
      ii.ttt;
    end;procedure RegApp(App:TApplication;out intf);stdcall;
    begin
      _App := App;
      Supports(TMyDLLTest.Create,IMyDLLTest,intf);
    end;{ TMyDLLTest }procedure TMyDLLTest.Hello;
    begin
      ShowMessage(_App.MainForm.Caption);
    end;procedure DuoTest;stdcall;
    begin
      ShowMessage('DuoTest');
    end;exports
      Test,
      RegApp,
      DuoTest;
      
    begin
    end.
      

  18.   


    unit uBPL;interfaceuses Dialogs;procedure DuoTest;implementationprocedure DuoTest;
    begin
      ShowMessage('DuoTest');
    end;end.
      

  19.   

    谢谢,你的实现方法不错,但是我只是想比较动态使用BPL与DLL,想知道这种情况下BPL有没有优势,如果DLL一样能做到的功能就不用提了。
      

  20.   

    脱离具体的应用场景来谈优势是不行的,因为各有各的用途。可能是你的问题描述得不准确吧,我想你的意思只是,针对你的那个例子,有没必要用BPL而已。而不是动态加载的问题,动态加载可以讨论的东西太多了。
      

  21.   

    不管是静态还是动态加载,BPL都比DLL有优势,应该优选BPL方式。除非你的系统中,有用其他语言开发的子程序,不“认识”BPL的时候,才使用DLL。BPL的唯一缺点,就是它是与Delphi开发版本相关的,一般不能混用。如果你的BPL不需要Delphi的基础BPL包的支持,比如纯是你自己的函数库,则无此问题。
      

  22.   

    请ddqqyy说明动态加载BPL的优势
      

  23.   

    请谈一下什么应用场景下动态加载BPL比DLL有优势,不局限在15楼我贴的那截代码(谁可以把15楼代码删除啊)。
      

  24.   

    DLL比BPL好的地方:
    1、DLL 语言无关,自然与Delphi版本无关了。 BPL则不一样,D2010编译的程序加载D7编译的BPL是会出错的。
    2、DLL可以被其他语言调用。BPL仅限于对应版本的Delphi编译器。BPL的好处,无论动态加载或静态加载:
    1、多个BPL可以方便自然地共享全局变量。DLL不能或者说要很复杂地做才能在多个DLL间共享全局变量。
    2、类的支持。BPL有类的信息,可以方便地从其他BPL中继承类,包括Form继承。DLL不行。
    3、BPL中可以地选择延迟加载其他DLL,编程起来与静态加载一样,DLL中加载其他DLL时,只能作静态或动态二选一。说的不一定对,只是这个问题与动态或静态加载关系并不大。
      

  25.   

    BPL对编译器版本的依赖不是绝对的,需要例子我可以提供。
    只谈动态加载,我对你提出的“BPL的好处 1 2 3”都很好奇,能否描述一下使用方法或代码示意一下。
    “只是这个问题与动态或静态加载关系并不大”,这句话我真的不赞同。
      

  26.   

    靜態加載BPL,可以做到的功能。動態加載BPL也能做到。靜態加載BPL時,delphi的程序可以很方便地選擇編譯成一個EXE不帶包運行,也可以選擇編譯成帶包運行的版本。這個也是BPL的一個優點,DLL是沒得選擇的。只是樓主只想說動態的,所以這個優點也就不能算了。BPL與DLL的差別,無論靜態或動態加載,其差別都幾乎一樣。因為無論BPL或DLL自身對比,靜態或動態加載實在區別不大,只是編程者選擇加載時刻而已。如果只是函數重用,用DLL很好。如果是類重用及繼承,BPL比DLL好很多。 
      

  27.   

    That is correct. A BPL is a DLL. (But not all DLLs are BPLs.)  > But I still found some different, such as that I can create a  
    > object from the Host exe and that pass to a BPL and modify it safely, but  
    > if I do same to a dll, I can not modify any referenced property of the object.  When you use packages, there is only ever one copy of any unit in  
    memory. One copy of Forms, one copy of SysUtils, one copy of System  
    (well, most of it), one copy of StdCtrls, etc.  All class-related operations, such as the "is" and "as" operators, rely  
    on class references. Class references are actually just addresses. They  
    point to definitions for the layouts of the classes' internals. (They  
    point to what's called the virtual-method table, the VMT.) Two classes  
    are the same if they point to the same VMT -- if the addresses are equal.  When you have a class defined in the EXE's copy of StdCtrls and the same  
    class defined in a DLL's copy of StdCtrls, those classes will really  
    have different addresses. The "is" and "as" operators won't work with  
    cross-module clases. But when you use packages, there is only one copy  
    of the class, kept in vcl70.bpl, so all modules that reference that  
    package will share a single class definition.  Another factor is the memory manager in System. All string allocations  
    ultimately call GetMem and FreeMem. If the EXE allocates a string, it  
    uses its own GetMem. It then passes the string to a DLL, and the DLL  
    might try to free it. The DLL will call its own copy of FreeMem, which  
    won't have access to the EXE's memory-manager structures, and you'll get  
    errors. With packages, everything will use the same memory manager from  
    rtl70.bpl. (This can also be solved by using a shared memory manager  
    between the EXE and the DLL; ShareMem is one example. That won't solve  
    the class-comparison problem, though.)  
      

  28.   

    果一定要只比较动态加载,我觉得意义不大。当然,如果BPL是要完全动态加载的话,一般是要拆分成接口和实现包,静态依赖接口包,动态加载实现包。常需要使用FindClass,根据名称来找接口实现。可以做成配置来动态决定加载哪个包里面的实现。可以了解下IOC思想,如spring容器等,其思想的实现也会用到BPL。
      

  29.   

    bpl对于局域网来说其实是优于bpl的,更效率些。
    dll更容易被其他语言调用。
      

  30.   


    请教一下, 动态加载时,全局变量如何共享?  编译时经常提示我AAA.pas已包含BBB.pas.