找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 4878|回复: 1

【分享】Delphi – 在DLL中保存并创建MDI子窗体(翻译)

[复制链接]

1214

主题

352

回帖

11

精华

管理员

菜鸟

积分
93755

贡献奖关注奖人气王精英奖乐于助人勋章

发表于 2009-7-26 12:48:58 | 显示全部楼层 |阅读模式
<font face="Verdana">很多人认为,在Delphi的DLL中加载MDI子窗体时,需要注意保存并恢复Delphi全局变量(Application和Screen)对象。其实,根本没有办法在DLL中加载MDI子窗体。<br/>原文出处:http://delphi.about.com/library/weekly/aa020805a.htm<br/>&nbsp;&nbsp;&nbsp; 在开发复杂的应用系统时,把系统分为各个模块分而制之是一种很平常的策略,这样就可以使得每个模块负责一套业务逻辑。模块可以是一个DLL,在需要的时候,主程序可以调用这个DLL以使用该模块。<br/>&nbsp;&nbsp;&nbsp; 当你开发一个MDI应用程序,并希望把该应用程序分割成各个模块时,自然会有这样的疑问:“如何将MDI子窗体创建在DLL中,以使得在必要的时候能够被主程序调用?”<br/>&nbsp;&nbsp;&nbsp; 如果你已经知道如何在DLL中创建非MDI子窗体的窗体,并且也知道如何从主程序调用这个DLL中的窗体时,你或许会认为问题已经得到了解决,但事实并非如此。<br/>&nbsp;&nbsp;&nbsp; 如何让DLL中的MDI子窗体得知哪个窗体是它的父窗体(以便让其能够以子窗体的形式展现出来)?用于创建该子窗体的Application对象(位于父窗体所在的应用程序中)与你在DLL中获得的Application对象根本就是两回事情,不仅如此,两者的Screen对象也不相同。<br/>想要在DLL中保存并创建MDI子窗体?没门!<br/>&nbsp;&nbsp;&nbsp; 就如你所看到的这个标题所说的一样,根本就没有办法在DLL中保存并创建MDI子窗体,然后让MDI应用程序去调用它。或者你会说,你已经在网上找到一些资料,上面显示如何保存并传递Application对象,以使得DLL中的MDI子窗体能够被创建于正确的MDI父窗体中,其实这种做法并不奏效,至少在Delphi 5以上的版本中不会奏效。<br/>那么解决方案是什么呢?<br/>&nbsp;&nbsp;&nbsp; 如果你非要在DLL中保存并创建MDI子窗体,那么你需要在主程序(MDI应用程序)和DLL生成的时候,与运行时包(runtime package)一起生成。这样做就确保了主程序和DLL使用了共同的Application和Screen对象,并使用了相同的RTL与VCL实例。为了100%确保“安全”,你应该使用包,而不是DLL。<br/>包中的MDI子窗体:唯一正确的解决方案<br/>&nbsp;&nbsp;&nbsp; Delphi的包是一种特殊的DLL,它仅用于Delphi的应用程序。如果你的应用程序模块是使用包的形式开发而不是DLL,那么所有的模块都会共享同一个内存管理机制,包括了VCL全局对象(例如Application和Screen)以及RTL与VCL在内存中的同一代码拷贝。<br/>包中的MDI子窗体:实例<br/>&nbsp;&nbsp;&nbsp; 现在来看一个实例,首先,我们将创建一个MDI应用程序:<br/>1、 创建一个新的MDI应用程序。你可以使用MDI应用程序创建向导(File - New - Other - Projects - MDI Application)<br/>2、 确保主窗体的FormStyle属性已经设置为fsMDIForm<br/>3、 添加一个MainMenu控件,使其只有一个用于从包中加载子窗体的菜单项<br/>4、 确保在生成应用程序的时候,是和运行时包一起生成的。在Project – Options菜单中,选择Packages选项卡,然后选中“Build with run-time packages”选项。你至少要选中rtl包和vcl包<br/>在真正编码前,首先生成该包,并且向其添加一个MDI子窗体<br/>1、 创建一个新的运行时包<br/>2、 向包添加一个TForm对象,确保该对象的FormStyle属性已经设置为fsMDIChild<br/>3、 添加一个导出过程,用于创建子窗体的实例:<br/>procedure TPackageMDIChildForm.FormClose<br/>(Sender: TObject; <br/>&nbsp;&nbsp; var Action: TCloseAction);<br/>begin<br/>//since this is an MDI child, make sure <br/>//it gets closed when the user <br/>//clicks the x button.<br/>Action := caFree;<br/>end;<br/>procedure ExecuteChild;<br/>begin<br/>TPackageMDIChildForm.Create(Application);<br/>end;<br/>exports<br/>//NOTE!! The export name <br/>//is CASE SENSITIVE<br/>ExecuteChild;<br/>end.<br/>&nbsp;&nbsp;&nbsp; 重新回到MDI主程序,以下是MDI主窗体的所有代码:<br/>type<br/>//signature of the "ExecuteChild"<br/>//procedure from the Package<br/>TExecuteChild = procedure;<br/>TMainForm = class(TForm)<br/>&nbsp;&nbsp; ...<br/>private<br/>&nbsp;&nbsp;&nbsp; PackageModule : HModule;<br/>&nbsp;&nbsp;&nbsp; ExecuteChild : TExecuteChild;<br/>&nbsp;&nbsp;&nbsp; procedure PackageLoad;<br/>end;<br/>var<br/>MainForm: TMainForm;<br/>implementation<br/>{$R *.dfm}<br/>procedure TMainForm.PackageLoad;<br/>begin<br/>//try loading the package<br/>//(let's presume it's in the same<br/>//folder, where the main app. exe is)<br/>PackageModule := LoadPackage('MDIPackage.bpl');<br/>//if loaded, try locating<br/>//the ExecuteChild procedure<br/>if PackageModule &lt;&gt; 0 then<br/>try<br/>&nbsp;&nbsp;&nbsp; @ExecuteChild := GetProcAddress(PackageModule,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'ExecuteChild');<br/>except<br/>&nbsp;&nbsp;&nbsp; //display an error message if we fail<br/>&nbsp;&nbsp;&nbsp; ShowMessage ('Package not found');<br/>end;<br/>end;<br/>//menu click<br/>procedure TMainForm.mnuCallFromDLLClick<br/>(Sender: TObject);<br/>begin<br/>//lazzy load package<br/>if PackageModule = 0 then PackageLoad;<br/>//if the ExecuteChild procedure<br/>//was found in the package, call it<br/>if Assigned(ExecuteChild) then ExecuteChild;<br/>end;<br/>procedure TMainForm.FormDestroy(Sender: TObject);<br/>begin<br/>//if the package was loaded,<br/>//make sure to free the resources<br/>if PackageModule &lt;&gt; 0 then<br/>&nbsp;&nbsp;&nbsp; UnloadPackage(PackageModule);<br/>end;<br/>&nbsp;&nbsp;&nbsp; 上面的代码中,选中菜单项时,主程序动态地加载了所需的包(使用PackageLoad过程),并且在应用程序终止的时候卸载了已经加载的包。最后,在运行时,我们得到了一个能够正确加载并运行包中MDI子窗体的应用程序。<br/>&nbsp;&nbsp;&nbsp; 最后需要说明的是,在使用runtime package模块化应用程序时,你必须将所需的包与应用程序的exe文件一起发布。</font>
【VB】QQ群:1422505加的请打上VB好友
【易语言】QQ群:9531809  或 177048
【FOXPRO】QQ群:6580324  或 33659603
【C/C++/VC】QQ群:3777552
【NiceBasic】QQ群:3703755

1214

主题

352

回帖

11

精华

管理员

菜鸟

积分
93755

贡献奖关注奖人气王精英奖乐于助人勋章

 楼主| 发表于 2009-7-26 12:52:03 | 显示全部楼层

用DLL方式封装MDI子窗口

<font face="Verdana">用DLL方式封装MDI子窗口<br/>DLL方式封装MDI子窗体是一种常用的软件开发技术,它的优点:<br/>人员可以负责某一个模块的编写包括(界面+逻辑),可以互不干扰,模块开发完成后,主程序统一调用。<br/>程序升级,当程序升级时,不用编译主exe文件,更新某个DLL就可以升级。可以根据客户不同的需求、价钱给他们不同的模块。(奸商都愿意用这招) 用到这个技术,因为小弟没有用过,在网上google了一下,对它有了了解,我用的是基于COM的DLL,下面总结如下:<br/>想在DLL中封装MDI子窗体,要注意以下几点:<br/>你的DLL中的接口要有两个参数:第一,主程序的Application对象;第二,主程序窗体对象;你要重写DLL入口函数,如果是基于COM的,要注意:因为COM没有TApplication和TForm类型,你在传入时需要强转为Integer。需要注意的就这么多,下面结合例子看看(假设你已经建立了工程):<br/>TComDllTest1 = class(TAutoObject, IComDllTest1)<br/>Private<br/>FMsgStr WideString;<br/>function GetMDIForm(AForm TForm) boolean;<br/>protected<br/>{ Protected declarations }<br/>function Get_MsgStr WideString; safecall;<br/>procedure Set_MsgStr(const Value WideString); safecall;<br/>procedure ShowMsgStr; safecall;<br/>procedure CreateForm(AHandle SYSUINT); safecall;<br/>procedure CreateMdiForm(var AApp; AForm SYSINT); safecall; 创建MDI窗体 SYSINT为unsigned int<br/>procedure CreateComTest(AForm SYSUINT); safecall;<br/>public<br/>constructor Create(AForm SYSINT);<br/>end;<br/>这是CreateMdiForm方法的实现:<br/>procedure TComDllTest1.CreateMdiForm(var AApp; AForm SYSINT);<br/>var<br/>app TApplication;<br/>af TForm;<br/>begin<br/>app = TApplication(AApp); 将传进来的Application对象强转<br/>af = TForm(AForm); 将传进来的Form对象强转<br/>Application = app; 将主程序的application付给COM工程的application对象<br/>if not GetMDIForm(Form1) then GetMDIForm 是判断窗体是否已经创建<br/>begin<br/>Form1 = TForm1.Create(af); 创建子窗体<br/>Form1.FormStyle = fsMDIChild;<br/>Form1.Show;<br/>end;<br/>end; <br/>最后一步也是最关键的就是重写DLL入口函数,如果不重写,关闭主窗体时会报地址错误,我就吃过这亏(我汗......),在DLL工程文件中写:<br/>procedure DLLUnloadProc(Reason Integer); register;<br/>begin<br/>if (Reason = DLL_PROCESS_DETACH) or (Reason = DLL_THREAD_DETACH) then Application = DLLApp; DLLApp是在DLL工程文件中定义的全局TApplication对象<br/>用来保存Application对象<br/>end;<br/>在DLL初始化的位置加入:<br/>DLLApp=Application; 保留pplication<br/>DLLProc = @DLLUnloadProc; 将重写后的入口函数地址付给DLLProc<br/>这样就实现了DLL封装MDI子窗体,当然你要在子窗体上进行比如:数据库的操作,还要有更丰富的界面,肯定会遇到各种问题。以后总结了再说吧!</font>
【VB】QQ群:1422505加的请打上VB好友
【易语言】QQ群:9531809  或 177048
【FOXPRO】QQ群:6580324  或 33659603
【C/C++/VC】QQ群:3777552
【NiceBasic】QQ群:3703755
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表