EXE+BPL (动态加载bpl).例如:
一种情况是:
用户的登录信息存在EXE的User_Rec变量中, 动态加载的BPL中需要检测这个User_Rec中的相关信息.另一种情况:
EXE加载login.bpl, login.bpl中保存User_Rec中. 其它BPL如何读User_Rec?
一种情况是:
用户的登录信息存在EXE的User_Rec变量中, 动态加载的BPL中需要检测这个User_Rec中的相关信息.另一种情况:
EXE加载login.bpl, login.bpl中保存User_Rec中. 其它BPL如何读User_Rec?
解决方案 »
- 有没有简单的方法吧所有挂起的线程都唤起呢
- 120分 求禁止DBLookupComboboxEh1的自动填充功能的方法
- 请问各位高手,如何在报表打印中设定打印的内容到了一定的行数以后就可以自动换列???(在线等)
- 大家帮个忙!!急!
- 如何编写用来执行sql的函数????
- 急!!SQL语句在发送到数据库前的校验?检查基本的语法是否正确!!
- 关于开发酒店管理系统的一些知识来者有分
- 如何实现让DLL与EXE共享一块内存空间?我会继续加分直到实现
- 请问:数据库货币类型与实型的转换?
- 入门级ado问题!!!!!
- fast report 3 打印预览时保存成 word 文件,原来的格式全乱了,求解!!!
- Delphi如何以指定的格式将字符串转换为TDateTime?
并且,最好保证login.bpl最先被加载
同意楼上的:
其他包或EXE在Requires中加入login.dcp,然后引用相关单元即可。
如果不加入login.dcp,则动态加载时会提示XX.pas已经被包含在某个包中,从而加载失败。
然后,但凡需要这个变量的(包括exe)都引入这个 bpl。
options / build with package(exe),
requirs 加入 dcp。
很简单的。
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楼,把别人的意图看清楚你再否定。
例如:
下面是一个包的定义:
(注意,这个包没有主动导出任何一个函数)
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 部分的内容都导出来了。
那麼: 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...及其他動態包 做好隔離。
EXE中 uses bpl 中包含的x.pas, 并且add bpl的dcp时, 会动态检测bpl是否存在,(但未加载), 当EXE未uses bpl中包含的x.pas时, 是不会检测的.稍后再结贴
是A bpl uses b bpl 中的单元,exe通过LoadPackage 加载 b 然后再加载A 这时候, A 就能使用 B中的变量了。
否則,就不是動態加載了。關於BPL靜態包動態包,我多說幾句:靜態包是一個體系。動態包也自成一個體系。
不成類體系的動態包,與DLL方式無多大差別。也就是函數重用使用DLL,類重用使用BPL,這是BPL、DLL設計初衷。也就是兩者之最大差別。靜態包與動態包之間只需要"單向物理隔離"。約束只在EXE及靜態包這端,編寫BPL時沒有約束,使得BPL中要重用其他BPL的內容極為方便(想要在EXE或靜態包中引用動態包除外)。哪些包將被靜態加載,哪些包將被動態加載這個需要規劃,因為靜態包與動態包之間有一條"單向的鴻溝"。這條鴻溝將靜態包與動態包劃分成兩個陣營。兩個陣營之間關聯的紐帶,需要程序員去設計。也就是常見的代理,托管,工廠啊之類的常見模式。在強類型語言中,RTTI等屬於Crack侵入式調用范疇,盡量少用。
一般系統自帶的BPL被靜態加載。自寫的全局共用的BPL被靜態加載(也可以動態),自己寫的多個BPL體系,則設計成動態加載。