Delphi中的包(一):关于exe的编译、连接和执行

2016-02-19 21:32 3 1 收藏

下面请跟着图老师小编一起来了解下Delphi中的包(一):关于exe的编译、连接和执行,精心挑选的内容希望大家喜欢,不要忘记点个赞哦!

【 tulaoshi.com - 编程语言 】

  最近搞Delphi,发现原来的程序中使用了很多包,但是总是处于懵懵懂懂的状态。索性来好好研究一下这个问题,可能要花一些时间。所以首先把需要分析的问题列出来:
  什么是包?什么是exe?它们在组成上有什么不同?包跟dcu是什么关系?dcp是干什么的?这些文件在编译时是什么关系?又是怎么装载的?装载了以后怎么样操作包?dll可以exports,但是为什么delphi帮助中不提包的exports,但是有些代码却又在包中使用exprots?

  首先来看看delphi的编译过程。delphi的工程中有两类:包和程序,前者的后缀为dpk,后者为dpr。从简单的开始,先来搞dpr。根据delphi的帮助文档,一个典型的dpr文件的结构如下:
   1 program Editor;
   2    
   3     uses
   4       Forms, {change to QForms in Linux}
   5       REAbout in 'REAbout.pas' {AboutBox},
   6       REMain in 'REMain.pas' {MainForm};
   7    
   8     {$R *.res}
   9    
  10     begin
  11       Application.Title := 'Text Editor';
  12       Application.CreateForm(TMainForm, MainForm);
  13       Application.Run;
  14     end.
  其中10行到14行,begin…end很自然就是程序的执行入口。uses部分指明了程序需要使用的一些Unit,这个就比较含糊了,为什么有的会用in指明源代码的位置(这部分是自己向工程中添加的),有的如Forms这个部分,却又不需要?那每个Unit又会uses其它Unit,这个问题似乎越来越复杂了。先看整个源代码的结构:
  
  我猜测编译器第一步首先遍历这张有向图,对每个Unit,如果有必要就对其进行编译,生成对应的dcu。而这个“必要”问题,我开始以为是use这个Unit的语句是带有in的,后来试验发现不对。因为在上面的情况下,Unit3并没有在Unit1的Uses子句中指明路径,但是仍然正确产生了对应的dcu文件。后来使用filemon来监视文件打开情况,发现过程是这样的:对于图中的每个节点,编译器按照当前目录—project属性中的search path—IDE环境中的library path,这样的顺序,搜索节点对应的pas文件,没找到就再来一遍,但是这次搜索的是节点对应的dcu文件。

  现在编译搞定了,每个Unit(即pas文件)已经生成了对于的dcu文件,下面的问题是连接。说到连接,问题就复杂了,连接有两种:静态和动态。静态连接就是说把这些dcu全部合并到一起。这样,一个Unit对另一个Unit的调用,就成了程序内部的事情了。这样的好处是快,而且简单,并发共享之类的问题都容易处理。缺点是目标程序很大,而且如果现在要编写另一个程序,而Unit3可以重用的话,则在连接时Unit3.dcu被再次拷贝。这样在两个程序同时运行时,内存中会有两个Unit3的副本,比较浪费。动态连接就是说,两个程序在连接时,仅仅只保留对Unit3的引用,而并不拷贝Unit3的内容。到运行时,把Unit3装入内存,让两个程序公用。Dll和BPL都是动态连接的解决方案。问题在于,delphi中关于连接的选项就只有project|Options|packages菜单中出现,“Build with runtime packages”这句话实在是太模糊了。所以还要再研究一下。
  在程序执行的时候,我们可以通过view|debug window|moudles来查看有哪些东西被加载到内存中去了,它们又包含哪些内容。
  简便起见,我们建立如下结构的一个程序:
  

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  program ProjectEXE;

  
  uses
    Forms,
    Windows,
    UnitFormMain in 'UnitFormMain.pas' {FormMain};

  {$R *.res}

  begin
    Application.Initialize;
    Application.CreateForm(TFormMain, FormMain);
    Application.Run;
  end.

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  unit UnitFormMain;

  interface

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  uses
    Windows, StdCtrls, Forms, UnitFormAnother,Classes, Controls;

  type
    TFormMain = class(TForm)
      Button1: TButton;
      procedure Button1Click(Sender: TObject);
    private
      { Private declarations }
    public
      { Public declarations }
    end;

  var
    FormMain: TFormMain;

  implementation

  {$R *.dfm}

  procedure TFormMain.Button1Click(Sender: TObject);
  var
    LForm:TFormAnother;
  begin
    LForm:=TFormAnother.Create(Application);
    LForm.ShowModal;
    LForm.Free;
  end;

  end.

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  unit UnitFormAnother;

  interface

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  uses
    Forms;

  type
    TFormAnother = class(TForm)
    private
      { Private declarations }
    public
      { Public declarations }
    end;

  implementation

  {$R *.dfm}

  end.

“Build with runtime packages”不打钩的时候,是静态连接的。有向图中出现的所有Unit都包含在目标文件中了,整个exe有356k,而两个Unit各自只有4k。
  现在来动态。“Build with runtime packages”打钩,现在发现运行时ProjectEXE.exe文件只包含四个部分:两个Form、一个SysInit.pas、一个ProjectEXE.dpr;与此同时进程树里面多了两个bpl:rtl60和vcl60,它们的内容就是刚才静态连接中出现的那些Unit。现在ProjectEXE.exe只有16k。也就是说,有向图中的Unit,一部分放在exe中了,另一部分放在bpl中了。但是根据什么来划分呢?是根据uses子句,还是根据这里“Build with runtime packages”中的列表?继续测试,发现:如果列表中仅包含vcl60,则加载到内存中的还是两个bpl加一个exe;如果列表中只包含rtl60,则内存中仅包含rtl60和exe,但是exe的内容发生了变化:里面的Unit增多了,而且基本都是vcl60包里面的。我猜想应该是rtl和vcl包之间存在require关系。这个留到下一步再测试。但是初步估计连接过程中,肯定会利用包列表,将那些已经在包中存在的Unit从exe中排除出去。

  

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  在动态连接之后,还存在一个问题:装入。装入有两种策略,静态也称为自动,由delphi生成代码,在装载exe之前,自动装入包;另一种是动态,即在程序运行时通过编码,指定一个包,把它装入内存。问题在于,我必须搞清楚delphi在什么情况下会自动装入一个包,什么情况下可以避免delphi自作聪明,这样才能灵活地使用包。前面的试验中,只可以看出,在dpr文件执行到begin之前,静态连接的的包就已经装入内存了。具体过程我也不清楚,等下一章开始写自己的包,再来做实验吧。


来源:https://www.tulaoshi.com/n/20160219/1626424.html

延伸阅读
       关于Linux下编写和编译程序的几个问题   · 闫健勇·CPCW       当前,虽然Linux还不很普及,在Linux下编写和编译程序的人不多。但是我相信,随着Linux性能的不断提升和逐渐普及,会有许多自由软件出现,也会有许多人成为Linux下的程序员。我结合自己的经验,介绍一...
第5部分 编译文件(第12页) 我们的下一个例子是一个在IDE中用VCL(可视化组件库)编写的程序。这个程序自动的形成框架窗口和资源文件,所以你不能从单一的源文件编译。但是他说明了delphi语言的一个重要的特性。除了多单元外,可以使用类和对象。 这个程序包括一个工程文件,和2个新的单元文件。首先,工程文件如下:program greeting;u...
标签: Web开发
熟悉javascript的朋友对Eval()函数可能都不会陌生,我们可以用它来实现动态代码的执行,我自己甚至写过一个网页专门用来计算算术表达式的,计算能力上比google、baidu的计算器还要好一些,至少精度要高,但是如果超出了四则运算的话,表达式的形式会复杂很,比如以百度给出的例子: log((5+5)^2)-3+pi需要写成Math.log(Math.pow(5+5,2))*Math....
标签: Delphi
  《关于VisiBroker For Delphi的使用》——CORBA技术实践(三) 宜昌市中心人民医院 赵普昉 email: 3boy@sohu.com 三、数组对象与简单数据对象的传递 前面提到了一些较为简单的数据操作,我们都可以想象一下,如果操作CORBA对象与操作C/S结构的数据对象一样的方便,那么CORBA又有什么神奇了,不知道看过李维的分布式多层应用系统...
在Delphi中获取和修改文件的时间 本文介绍了在Delphi中利用系统函数和Windows API函数调用来获取和修改文件的时间信息的方法。 熟悉Windows 95/98的朋友一定经常会用单击鼠标右键的方法来查看所选定的文件的属性信息。在属性菜单中会列出该文件的创建时间、修改时间和访问时间。这些信息常常是很有用的,它们的设置一般都是...

经验教程

351

收藏

30
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部