EXE+BPL (动态加载bpl).例如:  
一种情况是: 
用户的登录信息存在EXE的User_Rec变量中, 动态加载的BPL中需要检测这个User_Rec中的相关信息.另一种情况: 
EXE加载login.bpl, login.bpl中保存User_Rec中. 其它BPL如何读User_Rec?

解决方案 »

  1.   

    由exe把user_rec的指针传递给各个bpl,或者写入注册表键值各个bpl读取,还有可以写入磁盘文件读取
      

  2.   

    如果你的登陆信息放在bpl中,那么其他bpl只需要uses你的login.bpl中保存登陆信息的单元,然后使用聚是了,但是前提是,你那些引用登陆信息的bpl需要在Requires中加入login.dcp
    并且,最好保证login.bpl最先被加载
      

  3.   

    login.bpl中保存User_Rec中. 其它BPL如何读User_Rec?
    同意楼上的:
    其他包或EXE在Requires中加入login.dcp,然后引用相关单元即可。
    如果不加入login.dcp,则动态加载时会提示XX.pas已经被包含在某个包中,从而加载失败。
      

  4.   

    把两个变量变成一个,要么都放到 Login 里,要么再弄个 “第三个bpl”。
    然后,但凡需要这个变量的(包括exe)都引入这个 bpl。
    options / build with package(exe),
    requirs 加入 dcp。
    很简单的。
      

  5.   

    build with package -》add 加入x.dcp后,x.bpl就变成静态加载了。(也就是EXE运行时,会立即检测x.bpl文件)
      

  6.   

    6楼的,你搞错了,是bpl之间需要引用dcp而不是exe引用dcp,exe仅仅是起到调度的作用,就类似一个框架。
    exe不需要静态引用任何在包中的单元(也就是不需要静态加载),并且在编译exe的时候要带包编译。
    在exe运行起来的时候,第一个动态加载login.bpl用来完成登陆,登陆验证成功后,再加载需要加载的其他bpl。
    这些后续被加载的bpl,如果需要使用登陆信息,则在自己的引用单元中需要引用带有登陆信息的那个单元并指定相关的dcp还有,bpl的动态加载,你要记住加载的顺序,也就是说如果你加载了A、B、C,则卸载的时候要按照C、B、A的顺序卸载,特别是要注意那些隐含加载的包,比如A包引用了B包,那么,你加载A包的时候,B包会先于A包加载,这时候,你卸载的时候就要注意了一定要谁后加载谁先卸载(也就是要先卸载A包再卸载B包)另外,如果你所有的exe、bpl都要无关联地动态加载,那你只能采用函数导出的方式了,这种形式的应用,使用bpl的意义不大。使用某项技术,是为了应用其长处为自己带来便利,如果单纯地就技术论技术,在实际的应用中非要用某项技术的短处实现另一项技术的长处,那是给自己找别扭。就如同使用bpl,我们问什么采用bpl而不采用dll,是因为我们要是有bpl给我们带来的便利。顺道说一下,bpl和dll的主要差别是bpl帮助你把你在bpl中所有单元中 interface 与 implementation 之间声明的数据类型都以导出函数的方式放在了导出表中,根据这些类型信息,你可以灵活地得到某个类或者某种你自己定义的数据类型的RTTI信息,这也是为什么bpl可以实现内存共享的原因。
    另外,bpl和dll的入口函数的处理方式是不一样的,bpl的入口函数只有一个跳转语句,直接跳转到SysInit单元的_InitPkg处,也就是说,当你加载bpl时,是从_InitPkg开始的。
    还有7楼,把别人的意图看清楚你再否定。
      

  7.   

    如果你一定要动态加载而所有的包都不相关联,那么也可以,很麻烦的。
    例如:
    下面是一个包的定义:
    (注意,这个包没有主动导出任何一个函数)
    package Package1;{$R *.res}
    {$ALIGN 8}
    {$ASSERTIONS ON}
    {$BOOLEVAL OFF}
    {$DEBUGINFO ON}
    {$EXTENDEDSYNTAX ON}
    {$IMPORTEDDATA ON}
    {$IOCHECKS ON}
    {$LOCALSYMBOLS ON}
    {$LONGSTRINGS ON}
    {$OPENSTRINGS ON}
    {$OPTIMIZATION ON}
    {$OVERFLOWCHECKS OFF}
    {$RANGECHECKS OFF}
    {$REFERENCEINFO ON}
    {$SAFEDIVIDE OFF}
    {$STACKFRAMES OFF}
    {$TYPEDADDRESS OFF}
    {$VARSTRINGCHECKS ON}
    {$WRITEABLECONST OFF}
    {$MINENUMSIZE 1}
    {$IMAGEBASE $400000}
    {$IMPLICITBUILD OFF}requires
      rtl,
      vcl;contains
      Unit4 in 'Unit4.pas';end.下面是包中唯一一个单元
    unit Unit4;interfacetype
      TLoginInfo = record
        UserID: Integer;
        UserName: array[0..31] of Char;
        Password: array[0..31] of Char;
      end;var
      a: Integer = 0;
      LoginInfo: TLoginInfo;procedure SMsg();implementationuses
      SysUtils, Dialogs;procedure SMsg();
    begin
      ShowMessage(Format('a:%d, uid:%d, UN:%s, UP:%S',
        [a, LoginInfo.UserID, LoginInfo.UserName, LoginInfo.Password]));
    end;end.这个单元很简单,定义了一个数据类型 TLoginInfo 定义了两个变量,定义了一个显示着两个全局变量的函数好了。编译这个包下面是在exe中动态加载这个包,并且得到两个全面变量并设置其值,然后调用包中的唯一的函数显示这些变量type
      TSMsg = procedure ;
      PLoginInfo = ^TLoginInfo;
      TLoginInfo = record
        UserID: Integer;
        UserName: array[0..31] of Char;
        Password: array[0..31] of Char;
      end;procedure TForm1.Button5Click(Sender: TObject);
    var
      hp: HMODULE;
      pInt: PInteger;
      SMsg: TSMsg;
      pLI: PLoginInfo;
    begin
      hp := LoadPackage('E:\ddss\Package1.bpl');
      if hp <> 0 then
      begin
        pInt := GetProcAddress(hp, '@Unit4@a'); //得到变量 a 的地址
        if pInt <> nil then
          pInt^ := 10;                            //给变量 a 赋值
        pLI := GetProcAddress(hp, '@Unit4@LoginInfo'); //得到结构体 LoginInfo 的地址
        if pLI <> nil then
        begin
          //给结构体 LoginInfo 的各个域赋值
          //这里需要注意,虽然这里定义的 TLoginInfo 和包中的 TLoginInfo 一摸一样,
          //但是,这是两个数据结构,在这里仅仅使用其一样的内存结构而已
          pLi^.UserID := 101;
          ZeroMemory(@(pLi^.UserName[0]), SizeOf(pLi^.UserName));
          ZeroMemory(@(pLi^.Password[0]), SizeOf(pLi^.Password));
          Move('hello', pLi^.UserName[0], 5);
          Move('123456', pLi^.Password[0], 5);
        end;    //得到显示包中的两个变量的函数的地址
        SMsg := GetProcAddress(hp, '@Unit4@SMsg$qqrv');
        if Assigned(SMsg) then
          SMsg(); //调用包中的函数
        UnloadPackage(hp);
      end;
    end;你可以看到,包没有使用exports关键字导出任何函数,但是我们却可以得到其中在 interface 域声明的数据类型和定义的变量。所以,包完全可以实现你所说的所有内容都动态加载并获得其中定义的相关数据,只要你有耐心找到其中的导出函数就行。但是,其中的麻烦程度我想你也看到了,另外,这种完全动态的方式,你觉得和dll有区别吗?唯一省事的是:包帮你将 interface 部分的内容都导出来了。
      

  8.   

    build with package -》add 加入x.dcp后,x.bpl就变成静态加载了。(也就是EXE运行时,会立即检测x.bpl文件)如果在EXE中引用 x.dcp , 確實是变成静态加载 . 動態加載的包可以直接引用動態加載的包,也可以直接引用靜態加載的包,但是在靜態包或EXE中不能引用動態包。靜態動態加載其實沒多大差別的,差別是靜態方便些,動態加載時,需要一個機制由靜態包(或EXE)來調用動態包中內容的方法(接口)。也就是當加載動態的BPL時,EXE文件知道如何調用它。其實差別就在於動態包隔離的方法,因為在靜態包(或EXE)中,動態包是不可見的,在代碼級別是隔離的。但在動態包中,無此限制。設: ABCDE 5個包, 一個EXE文件。EXE文件中鏈接的包AB為靜態包,准備動態加載的包為CDE.
    那麼: AB包中不能引用 CDE 等動態包中的內容,否則就讓CDE等包也是靜態加載的了。
    問:那如何使用 CDE 等動態包中的內容呢? 
      EXE做為調用主體,是要和動態包做隔離的,隔離的方法很多,諸如工廠模式,代理模式,動態鏈接方式等很多方法都可以做到隔離。常見的工廠模式,將類工廠放在EXE(或靜態包)中,動態包加載中在工廠中注冊即可。Delphi的RegisterClass , FindClass 系列方法,可以看成是一個簡單的類工廠實現。問:EXE如何訪問動態加載包中的 全局變量 ? 
        一般的設計,EXE無須直接訪問動態加載包中的全局變量。如果是在動態包中使用其他動態包的全局變量,如果不想隔離的話,直接引用即可。可是在EXE中,包是被隔離的,要使用放在包中的全局變量就成了問題,靜態包中的全局變量可以被動態包共用,動態包中的全局變量也可以被相關動態包共用,所以一般的設計是:將需要用到的全局變量放入靜態包中共用,或者分出一個專門的動態包供共用。
      非要在靜態包中使用動態包中的全局變量,怎麼辦? 因為包是被隔離的, 所以通過指針來引用是一個好的方法。一個簡單的方法是,在靜態包中放一指針,在動態包加載時由動態包初始化它,或者將全局變量放在靜態包中,只是由動態包來延遲初始化。另一個常見方法是通過函數返回。
        動態包中使用其他動態包的全局變量則是方便的。方便的是動態包使用靜態包的內容,反過來是不方便的。就如動態加載的DLL,EXE中怎麼可能靜態使用其中的變量喲。同一程序中多個包共享的全局變量往往被放在一個共享包中。這樣問題就很小的。DLL中共享全局變量可以到,但不是那麼好做的。要注意的問題,限制的地方太多了。完全动态的方式,和dll是有区别的。動態的BPL可以方便地共享訪問其他靜態或動態包中的變量,DLL中共享其他DLL的變量,可以做,但不好做。
    如果是要說在 EXE 中單向訪問動態包中的變量,同樣是隔離的,差別不大。"EXE加载login.bpl, login.bpl中保存User_Rec中. 其它BPL如何读User_Rec?"
    login.bpl 為動態加載 , 其他BPL也為動態加載的話,可以直接引用 login.dcp 的。
    但 EXE 要與所有動態加載的包 login.bpl...及其他動態包 做好隔離。
      

  9.   

    做個工廠模式 或者 代理類 來隔離包,應該不是一件難事,我認為這不用寫 sample 都能明白。wxieyang 也寫了用RTTI來得到動態包中方法及變量的代碼,只是那樣做還是比較麻煩的,沒有強壯性的,因為需要調用者知道如何調用,編譯器不作任何類型檢查。與調用DLL時不作任何類型檢查是一樣的。基於單元名稱找變量地址也不是提倡的做法。我建議是在靜態包中放一指針,由動態包加載時刻初始化該指針。這樣靜態包中不需要指定變量所在的單元,也就是不對動態包的組成做任何推測。
      

  10.   

    感谢 wxieyang 及 PPower的回复!
    EXE中 uses bpl 中包含的x.pas, 并且add bpl的dcp时, 会动态检测bpl是否存在,(但未加载), 当EXE未uses bpl中包含的x.pas时, 是不会检测的.稍后再结贴
      

  11.   

    既然你说动态加载了,为什么楼主总是想在exe中uses bpl中的单元呢?
    是A bpl uses b bpl 中的单元,exe通过LoadPackage 加载 b 然后再加载A 这时候, A 就能使用 B中的变量了。
      

  12.   

    动态加载要隔離,是代碼級別完全隔離,也就是EXE及其加載的靜態包不能引用動態包中的任何單元。
    否則,就不是動態加載了。關於BPL靜態包動態包,我多說幾句:靜態包是一個體系。動態包也自成一個體系。
    不成類體系的動態包,與DLL方式無多大差別。也就是函數重用使用DLL,類重用使用BPL,這是BPL、DLL設計初衷。也就是兩者之最大差別。靜態包與動態包之間只需要"單向物理隔離"。約束只在EXE及靜態包這端,編寫BPL時沒有約束,使得BPL中要重用其他BPL的內容極為方便(想要在EXE或靜態包中引用動態包除外)。哪些包將被靜態加載,哪些包將被動態加載這個需要規劃,因為靜態包與動態包之間有一條"單向的鴻溝"。這條鴻溝將靜態包與動態包劃分成兩個陣營。兩個陣營之間關聯的紐帶,需要程序員去設計。也就是常見的代理,托管,工廠啊之類的常見模式。在強類型語言中,RTTI等屬於Crack侵入式調用范疇,盡量少用。
    一般系統自帶的BPL被靜態加載。自寫的全局共用的BPL被靜態加載(也可以動態),自己寫的多個BPL體系,則設計成動態加載。