Delphi初学者应小心的六大陷阱,编写DLL动态链接
分类:计算机网络

  在DelphiI中新建一个程序,然后添加一个按钮,就得到了下面这段程序。这应该是大家相当熟悉的一段程序,可也就是这段程序,让许多的人在做开发很长时间后,还不能很好理解。

    而不需要这么写:
    Windows.MessageBeep(0);

一、DLL动态链接库文件的知识简介:

                  Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝。动态链接库就是在这种情况下出现的。动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可以被系统中的任何正在运行的应用程序软件所使用,而不必再将DLLs函数的另一拷贝装入内存。 任何应用程序都可以共享由装入内存的DLLs管理的内存资源块。只包含共享数据的DLLs称为资源文件。在Delphi中,一般工程文件的头标用program关键字,而DLLs工程文件头标用library 关键字标识(ActiveX控件也是一样)。不同的关键字通知编译器生成不同的可执行文件。用program关键字生成的是.exe文件,而用library关键字生成的是.dll等其他文件;假如要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在Exports子句中。而这些函数或过程本身必须用export编译指令进行编译。、

使用DLL动态链接库技术主要有以下几个原因:

1>、减少可执行文件的大小;

2>、实现资源共享;

3>、便于维护和升级

4>、比较安全

 

  那么如何才能避免这些错误了,尽量少走弯路了?笔者从事DelphiI开发多年,下面就把我的经验总结介绍给大家,希望帮助到初学DelphiI的朋友。

    Unit Unit1;

四、创建和调用DLL动态链接库的基本步骤:

1、点击【File】—>【New】—>【Other】菜单项,打开【New Items】,选择【New】;

2、选择【Dll Wizard】选项卡,点击ok,DLL工程创建成功。

3、添加代码。

4、按【Project】的【Build Project1】生成DLL动态链接库文件Project1.DLL。

5、调用DLL动态链接库文件。

                //调用程序和Project1.dll在同一个目录中,在implementation下面写, external后指定了Delphi.dll的位置

     1>、function TestDll(i:integer):integer;stdcall; external ‘Project1.dll’;

               //TestDll 必须跟Dll中函数名一样,区分大小写;Project1不区分大小写;

     2>、使用就跟普通的函数是一样的。  

 

  问题一:对类的概念理解不到位,程序开发中不能灵活运用。请看下面的程序:

    上例中,引用的单元都是标准单元,其中有的标准单元几乎是所有的单元都要用到的,因此Delphi自动把这些加到您的单元的Uses 部分,不过一些不太常用的单元,如果您的程序中要引用,您就必须自已手工把它们加到Uses部分。例如,在程序如果你需要使用sndPlaySound函 数来播放一个WAV文件,而这个函数是在mmsystem单元中声明的,因此,你需要在Uses 部分加入mmsystem,记得在加入在分号之前,与其他单元用逗号隔开。

二、DLL动态链接库文件的分类:

根据DLLs完成的功能,我们把DLLs分为如下的三类:

1、完成一般功能的DLLs;

2、用于数据交换的DLLs;

3、用于窗体重用的DLLs。

 

unit Unit1;
interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, shellApi;

type
TForm1 = class(TForm)
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
implementation

uses CommonUni;

二:子程序单元(Unit)
    子程序单元这种叫法只是为了和Program单元区别,由于大多数时候,我们几乎不需要接触Program单元,所以以后我们提到单元即是指子程序单元。
    Object pascal的一个单元,就是一个独立的Pascal源文件,扩展名为.Pas。单元的结构分为以下几个部分:

八、具体的一个例子:用DLL文件封装窗体的实现方法实例:

                       一个程序不再是单一的一个EXE文件了,而是由一个EXE文件加N个DLL文件组成,这样做的原因是方

便以后的维护与更新,也是跨平台开发的重要一步。

1、打开DELPHI,新建一个Dll Wizard

2、 在新建的Dll里新建一个Form

3、 在新建的Form里uses stdctrls

4、 在var下面写:

        Procedure synapp(App:THandle);stdcall;

        Procedure showform;stdcall;

5、然后在implementation 下面uses math

6、 在{$R *.dfm}下面写

Procedure synapp(App:THandle);stdcall;
Begin
  Application.Handle:=app;// 防止每显示一个窗体,就在任务栏中显示一个图标
End;

Procedure showform;stdcall;
Begin
  Form1:=Tform1.create(application);
  Form1.show;
End;

7 、在dll的Library文件里的{$R *.res}下面写:

exports
Sysapp,show;
上面到此为止完成了DLL封装窗体的创建

8、下面是调用了

1> 、 在要调用DLL文件的程序的var下写:

Procedure synapp(App:THandle);stdcall;external ‘my.dll’ ;//----你的DLL文件名

Procedure showform;stdcall;external‘my.dll’;//----你的DLL文件名

注:把你写好的DLL放在本程序的同一目录下,和上面一样,要uses math;

 

2> 、在你的程序的Button的On Click事件下写:

Synapp(applicatiln.Handle);

Showform;

 

完毕

 

                       用DLL文件封装窗体,每一个DLL工程中的窗体都是独立的一个进程。所以任何操作都是独立的。在DLL

工程中使用RegisterClass方法对窗体进行祖册是,在应用程序工程或者其他工程再用FindClass方法查找这个类是无

效的。而对于DLL工程而言,方法指针的传递非常的安全,所以可以维护一个指针列表,用于指向各个DLL工程中

FindClass方法的地址。在需要查找窗体类时,对所以的DLL工程的FindClass方法进行调用即可。

 

                       封装在DLL工程中的窗体,每打开一次窗体就会出现一个图标在任务栏区。为了解决这个问题,应在调用

DLL文件时,将应用程序中的Application对象和Screen对象传到DLL工程中,并替换DLL工程中这两个对象。

 

初学DelphiI的人,由于各种原因,对DelphiI中的许多概念不能很好的理解,并由此带来了许多的问题,或者是开发出的程序稳性不好,一会能运行,一会又不能运行;或者是遇到一个问题久思不得其解,还误以为是DelphiI自身的BUG,等等这些,浪费了我们大量的时间、精力,也影响了我们的开发效率。

    执行部分也称初始化部分,由保留字Begin和End括起来的一段代码。

七、使用DLL的实用技巧:

1、编写技巧:

1>、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL。

2>、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自

         定义非Windows定义的类型,如某种记录。

3>、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。

4>、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。

5>、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不

         引用Classes单元,这样可使编译后的DLL减小大约16Kb。

 

2、调用技巧:

1>、在用静态方法时,可以给被调用的函数或过程更名。改写引用函数为

          function   TestC(i:integer):integer;stdcall; external   'Project1.dll '     name   'TestDll ';

         其中name的作用就是重命名(原名称仍然大小写敏感)。 

         直接通过名称调用(注意名称大小写敏感)。     

         function   TestDll (i:integer):integer;stdcall; external   'Project1.dll '   ;

         //  如果定义了Index就可以使用,通过索引号调用。程序中可以用与DLL中不一样的名称.

        procedure   test2;external   'Project1.dll'   index   1;  // exports   TestDll    index   1;  

2>、可把我们编写的DLL放到Windows目录下或者Windowssystem目录下。这样做可以在external语句中或LoadLibrary

         语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与

         它们重名的话其后果简直不堪设想.

 

3、调试技巧:

1>、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿

         主程序。在Local页的Host Application栏中添上宿主程序的名字。宿主程序是使用它生成的DLL包的程序。然后

         再DLL工程中点击【Run】就可进行单步调试、断点观察和运行了。

2>、添加DLL的版本信息。如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如

         果直接使用Project|options菜单中Version选项是不行的,还必须增加{$R *.res},才会显示版本信息;

3>、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。

 

该程序可分为三个个部分:第一部分,单元头(从起始位置到TYPE之前);第二部分(从TYPE到END的部分),定义了一个从Tform继承过来的窗体类,它包含一个Tbuttton类型的成员。最后一部分(Var到结束的部分),定义了一个Tform1类型的变量。问题就出在这里了,许多人误以为这最后一段也是窗体类的一部分,在该窗体类中经常写出这样的代码,Form1.caption

’窗体标题’,导致程序运行时得不到所要的结果。其实最后一部分根本就属于窗体类的定义,它们不过是在同一个UNIT中而已,所以代码应该这样写:self.caption = ’窗体标题’;

  问题二:将释放对象的代码写在窗体的CLOSE事件中,导致Access Violation…的错误。

  一个窗体的关闭(CLOSE)与窗体的析构(Destory),在系统处理上是有区别的,当一个窗体关闭时,窗体实际上只是隐藏起来了,它占用的资源并未从内存中释放了,我们还是可访问到窗体中的数据;而当窗体响应DESTORY事件时,窗体不仅仅是隐藏起来了,而且占用的系统资源也释放出来了。因此,如果一个窗体关闭后,我们还想访里面的对象,就应该将这些对象的FREE代码写的窗体的(DESTORY)事件中。

  问题三:不加区别地使用String与shortString数据类型。

  String类型与shortString类型是有区别的,在默认的情况下(取决于$H开关),如果你将一个变量定义为string类型,那么会被处理成一个ANSIString类型。这种类型是动态分配内存的,以NULL为结尾,最大长度为4G,而shortString的最大长度是不能超过255个字符的。由于ANSIstring是生存期自管理类型的数据,这意昧着这种类型的数据需要更多的系统开销,所以在程序开发中,shortString能满足要求的话,就尽量使用它,以提高程序的运行速度。

  问题四:进行数据类型转换时处理不当,犯错误最多的就是字符型到数字/浮点型的转换。

  当将一个字符型数据转换为整型时,我们经常这样写 I := StrToInt(aEdit.Text); 表面上看这一句,没有任何问题,函数的使用,格式的写法,都是正确的。可有一种情况我们却没有考虑到,如果用户在aEdit文本框中输入的不是数字文本的话,会怎么样呢?调用还会成功吗?显然是不会的,系统肯定会弹出一个英文的错误,让我们的用户不知所措的。正确的写法是:I := StrToIntDef(aEdit.Text, 0); 这样当转换不成功时,第二个参数就会赋给I。类似的函数还有strToInt64Def,StrToFloatDef等等。

  问题五:单元引用的问题。使用那个函数,就一定要引用函数所在的单元。

  比如在程序开发中我们要用到一个API函数ExtractIconEx(从程序或是文件中获得一个图标),那么就一要在它的USES中把单元shellApi加入进来,否则是不能通过编译了。类似的情况还有很多,我们常常使用帮助文档,从中查找需要的函数,可当程序编译时,却通不过,为什么呢?就是因为没有在USES中引用函数所在的单元。这个问题初学者犯得最多,应该加倍注意。

  问题六:避免循环引用,尽可能通过第三个单元实现。如果确实不可避免,应在不同位置进行引用。所谓循环引用就是A单元引用了B单元,而反过来,B单元又引用了A单元,产生循环。我们还看上面的那一段程序,在interface的下面有一个USES语句,而在implementation的下面,又有一个USES语句。循环如果确实不可避免,那么就应该在将A单元中的引用写在第一个USES语句中,而将B单元中的引用写在第二个USES语句中。

...

一:Program 单元
    一个object  Pascal程序,是由一个特殊的单元和若干个可选的单元组成的,也就是说一个最简单的Object Pascal程序,可以只由一个特殊的单元组成,这个特殊的单元就是Program 单元。在Delphi中,Program 单元也就是Delphi的工程文件。Program 单元从功能上讲,有点像C语言中的主程序,程序再简单,它也得有一个Main函数。Program单元也就是这样的主程序。当然,一个程序如果只有 Program单元,它也没多大实际意义(除了一些完成特殊任务的程序外),事实上Delphi的程序至少有一个子程序,即后面要介绍的子程序单元。

六、在Delphi中调用DLL:

在Delphi中调用DLL动态链接库有两种方法:静态调用方法、动态调用方法;

1、静态调用DLL动态链接库(如上面给出的格式一样)

unit   Unit1;

interface

uses

Windows,   Messages,   SysUtils,   Classes,   Graphics, Controls,   Forms,   Dialogs,   StdCtrls;

type

TForm1   =   class(TForm)

Edit1:   TEdit;     // 编辑框(Edit)

Button1:   TButton; // 按钮(Button)

procedure   Button1Click(Sender:   TObject);

private

{   Private   declarations   }

public

{   Public   declarations   }

end;

var

Form1:   TForm1;

implementation

{$R   *.DFM}

// 本行以下代码为我们真正动手写的代码

function   TestDll(i:integer):integer;stdcall; external   ‘Project1.dll ';

 

procedure   TForm1.Button1Click(Sender:   TObject);

begin

Edit1.Text:=IntToStr(TestDll(1));

end;

end.

 

注意事项有以下一些:

1>、调用参数用stdcall。

       和前面提到的一样,当引用DLL中的函数和过程时也要使用stdcall参数,原因和前面提到的一样。

2>、用external语句指定被调用的DLL文件的路径和名称。

       正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果DLL文件在C:,则我们可将上面的引用语句写为external 'C:Delphi.dll '。注意文件的后缀.dll必须写上。

3>、不能从DLL中调用全局变量。

       如果我们在DLL中声明了某种全局变量,如:var s:byte。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。

4>、被调用的DLL必须存在。

       这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。

 

2、动态调用DLL动态链接库

只是将原来的Button1Click过程中的语句用下面的代码替换掉了。

procedure   TForm1.Button1Click(Sender:   TObject);

type

TIntFunc=function(i:integer):integer;stdcall; //定义一个函数类型

var

Th: Thandle;

Tf: TIntFunc;

Tp: TFarProc;

begin

Th := LoadLibrary( ‘Project1.dll');   // 装载DLL文件

if Th>0   then

try

Tp:=GetProcAddress(Th,PChar(‘TestDll’)); // 查找函数的位置

if   Tp<>nil then  

begin

Tf := TIntFunc(Tp);

Edit1.Text := IntToStr(Tf(1));   // 调用TestC函数

end

else

ShowMessage(‘TestDll函数没有找到 ');

Finally

FreeLibrary(Th);   // 释放DLL,否则会一直占用内存,知道退出windows或关机为止;

End

else

ShowMessage( 'Project1.dll没有找到 ');

end;

                      大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary( 'Project1.dll ')中的DLL名称为'Delphi.dll '就可动态更改所调用的DLL。

注意的事项有以下:

1>、定义所要调用的函数或过程的类型。

        在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestDll的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。

2>、释放所调用的DLL。

        我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。

 

3、两种调用方法之间的优缺点:

                       静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。

                       动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。

 

3:Implementation部分
    Implementation部分分为两大块,一块是声明部分,包括单元引用、常量、类型、变量、过程和函数的声明,这一点跟Interface部分相 似,所不同的是,在Implementation部分声明的单元引用、常量、类型、变量、过程和函数只对本单元是公共的、可见的,其它单元即使引用了该单 元,也不能访问它们。另一个不同点是,在Implementation部分声明的过程和函数,不需要遵循先声明后定义的规则,而只要直接写出过程或函数的 定义。

三、DLL动态链接库文件的基本格式如下:

library  Project1;   // 定义DLL文件的文件名,也是库名。和Unit差不多,会随保存时的文件名一起改变

uses

SysUtils,

Classes,

  Unit1 in 'Unit1.pas' {Form1}, // 创建的窗体文件

  Unit2 in 'Unit2.pas';   // 创建的单元文件

 

Type

   // 定义自己的数据类型

 

Var

   // 定义变量。

 

     // 自己定义的函数

function   TestDll(i:integer):integer;stdcall; // 与平时的编写差不多,只是多了一个stdcall参数

begin

Result := i+i;

end;

 

{$R *.res}  // 设置版本信息Project|options,必须有{$R *.res}才能显示。也可以位于函数的定义之前。

 

     // 自己定义的函数

exports   // 将函数或过程输出,供其他程序使用。不用写参数和调用后缀。函数直接用‘,‘分开;

      TestDll;

begin

end.

 

    编译指令,上例中,只有一个编译指令{$R *.RES},编译指令也是可选的。

五、编写DLL动态链接库时,应该注意的事项:

1、在DLL中编写的函数或过程都必须加上stdcall调用参数。

美高梅网址,                       在Delphi 1或Delphi 2环境下该调用参数是far。从Delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参

数。如果确实,就会变成register了。

 

2、所写的函数和过程应该用exports语句声明为外部函数。

                       正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(Quick View)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在Export Table栏中。另一个很充分的理由是,如果不这样声明,我们如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。

 

3、当使用了长字符串类型的参数、变量时要引用ShareMem,或者避免使用String类型。

                       Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:uses  ShareMem,  SysUtils,  Classes;

                       还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价? 避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。

    Implementation部分的另一大块是在Interface部分声明的过程和函数的定义,程序示例如下:

Program Project1;
Uses
  Forms,
  Unit1 in 'Unit1.pas';

    下面是一个示例:

    Initialization部分由保留字Initialization加一些语句构成,语句可以是单个语句,也可以是复合语句,如果是复合语句,应当用Begin和End括起来。

    当您保存文件时,您使用的保存文件名,将自动反映到这个部分。而且也会加到程序单元的USES部分中。因此,如果您工程中提示某文件缺失,你可以检查一下 这个文件的路径和名称是否改动过。DELPHI工程的文件是不必放在同一目录下的(但是我建议您这么做,便于管理)。

    引用部分,由保留字Uses加要引用的单元名组成,上例中有两个,分别是Forms单元和Unit1单元,
其中Unit1单元还指定在Unit1.pas文件中。最简单的程序可以没有USES部分。

    在Interface部分声明的引用单元、常量、类型、变量、过程和函数对整个程序是公共的,也就是对所有引用该单元的单元来说,这些声明都是可见的、可 访问的,例如引用该单元的单元就可以调用在Interface声明的过程和函数。Interface部分相当于类类型的Public部分。

    如果有多个单元含有Finalization部分,它们的执行顺序跟Initialization执行的顺序正好相反。

Begin
  Application.Initialize;
  Application.CreateForm(Tform1,Form1);
  Application.Run;
End;

{$R *.DFM}
End.

    在Interface部分,过程和函数只需写出它们的首部,具体的定义是在下面的Implementation(实现部分)给出的。

2:Interface部分 
    Interface部分称为接口部分,用于声明引用的、常量、类型、变量、过程和函数,单元的Implementa-tion部分(实现部分)也可以声明引用的单元、常量、类型、变量、那么这两者有什么区别呢?

    尽管这个例程是在Windows单元中声明的。
    注意:如果Interface部分有Uses部分,Uses保留字必须紧跟在保留字Interface后面,尽管可以不在同一行。

    Interface部分从保留字Interface开始到保留字Implementation前结束。

    下面列出一个典型的Program单元:

{$R *.DFM}
Procedure Beep;
Begin
  MessageBeep(0);
End;
Procedure Tform1.FormCreate(Sender:Tobject);
Begin
  Beep;
End;
End.

Unit Unit1;
Interface
Uses Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Diglogs;
Type
  Tform1=Class(Tform)
     Procedure FormCreate(Sender:Tobject);
  Private
  Public
  End;
Var
  Form1:Tform1;

    注意:单元首部也是一个完整的语句,因此单元名后应当跟分号,另外单元名必须符合Object Pascal关于标识符的规则,并且在同一个工程中单元名必须是唯一的。

    这是一个典型的Delphi自动生成的单元,它仅给出这个表单的结构。在Uses部分,引入了Windows,Messages,SysUtils,Classes,Graphics等单元,这样就可以直接调用这些单元中的例程,而无需加入单元引用名。例如:
    MessageBeep(0);

    Interface部分本身又可以由几个可选的部分组成,分别是单元引用部分(Uses)、常量声明部分、类型声明部分、变量声明部分、过程和函数声明部分。

{$R *.RES}

5:Finalization部分
    如果单元有Initialization 部分,这个单元才能有Finalization,终止部分的语法和初始化的部分是相似的,是由保留字Fnialization加一些语句构成的,语句可以 是单个语句,也可以是复合语句,如果是复合语句,应当用Begin和End括起来。

1:单元首部
单元首部类似于程序单元首部,由保留字Unit加单元名组成,如:

    程序首部,由保留字Program后跟一个程序名组成,上例中是Project1。程序首部也可以带有参数,
不过一般很少用到。

概要介绍:
Object Pascal语言的结构比较特殊,跟C有很大的不同,但是它秉承PASCAL语言的一贯结构化的传统,相信大家很容易就可以了解。

    单元引用部分(uses)用于列出该单元要引用的标准单元和其它单元。单元引用的概念有点类似于C语言中的INCLUDE,用于把外部的已声明过的常量、 类型、变量、过程或函数引入到本单元中使用,其中标准单元是指Object Pascal预定义好的单元,例如Windows单元、SysUtils单元、Forms单元等,您也可以把其它非标准单元加到Uses,这样该单元也就 能引用这些单元中的常量、类型、变量过程和函数等。当您往一个表单上加入控件时,该控件的单元将自动加到该部分,如果你还看不到,存盘就可以看到了。

    注意:用Delphi开发程序,除了一些特殊需要,如检查第二个实例是否运行,一般很少需要手工修改Program单元,因为Delphi能够自动建立和 维护这个单元,例如当您开始一个新的工种的时候,Delphi自动建立一个Program单元和一个子程序单元。当您向工程中加入了一个新的Form或 Unit,或者使用Project|Option…子命令修改了程序名,program单元将自动更新。

    如果有多个单元含有Initialization部分,它们的执行顺序和这些单元在Program单元的Uses 部分出现的顺序是一致的。

    加入到Uses单元的顺序一般是无关紧要的,但是有的单元如ShareMem单元必须放在第一个。
    最后强调一点,就是如果Interface 部分同时有这几个部分,一定要按照单元引用、常量声明、类型声明、变量声明、过程和函数的顺序排列。

首部
接口部分
实现部分
  初始化部分
  终止部分
End;

    Program单元由以下几个部分组成:

4:Initialization部分
    单元中可以有Initialization部分,这个部分也称为初始化部分,用于对该单元进行初始化。例如给变量赋初值,分配资源等,不过一般很少用到。

Implementation

Unit Unit1;
Interface
Uses Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Diglogs;
Type
  Tform1=Class(Tform)
  Private
  Public
  End;
Var
  Form1:Tform1;

Implementation

    注意:Finalization部分的代码应当能考虑到这样一种情况,就是单元的Initialization部分在执行过程中可能会被意外终止,也就是说可能会出现这么一种情况,就是有些指针的值是nil,那么在相应地终止,应当避免出现对这些指针的引用。

本文由美高梅网址发布于计算机网络,转载请注明出处:Delphi初学者应小心的六大陷阱,编写DLL动态链接

上一篇:试题收录,序列计数 下一篇:没有了
猜你喜欢
热门排行
精彩图文