资源预览内容
第1页 / 共485页
第2页 / 共485页
第3页 / 共485页
第4页 / 共485页
第5页 / 共485页
第6页 / 共485页
第7页 / 共485页
第8页 / 共485页
第9页 / 共485页
第10页 / 共485页
亲,该文档总共485页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
DelphiDelphi教程教程( (清华版清华版) )课程介绍课程介绍Delphi是著名的Borland公司开发的可视化软件开发环境,自1995年问世以来,带来了程序设计中的一场重大变化。Delphi作为第四代编程语言,具有简单、高效和功能强大等特点,这些特点使得它为广大程序开发者所青睐。第1章Delphi基础知识1.1 Delphi简介简介Delphi作为一种可视化的编程环境,经历了7代产品的发展历程:Borland公司在1995年推出了基于Windows平台的Delphi1。Delphi2新增加的主要功能是对32位编程的支持。还增加了如数据模块等多种高效的数据重用功能。Delphi3对其组件库进行增强,完全支持ActiveX组件及其创建。Delphi4提供了ObjectPascal语言的扩展,但系统稳定性较差。1999年推出Delphi5增强了数据库的功能2001年6月推出Delphi6,增加了大量的新组件,使组件数目达350多个,以满足网络开发的需要。2002年8月推出Delphi7,在集成开发环境IDE、Web、数据库、编译器、模型生成支持及组件库等很多方面进行了改进1.2 Delphi7 IDE1.2.1 认识集成开发环境认识集成开发环境Delphi7的IDE主要包括7个部分:主窗口、组件面板、工具栏、窗体设计器、代码编辑器、对象观察器和代码浏览器。图 1-1 Delphi 7的IDE1.主窗口主窗口主要包括:菜单栏、工具栏、标题栏和组件面板Delphi7的主菜单包括11个下拉菜单注意:工具栏上的按钮都提供了描述该按钮功能的tooltip,除了组件面板IDE有6个独立的工具栏IDE工具栏的定制功能并不仅限于配置需要显示的按钮,还可以调整工具栏、组件面板和菜单栏在主窗口中的位置。图1-2 Delphi 7的主窗图口Delphi7组件板包含了350多个组件,是Delphi可视化编程的核心部件,它由27个选项卡组成组件面板如图1-4所示包含了IDE中安装的所有的VCL组件和ActiveX组件将组件板上的组件添加到窗体上的三种方法:单击组件板上的所需组件的按钮,然后在窗体适当位置拖动鼠标画出组件,即可将组件添加到窗体的指定位置上图1-4 Delphi 7的组件面板双击组件板上的所需组件的按钮,即可将组件添加到窗体的中心位置按下键不放,单击组件板上的所需组件的按钮,所选组件出现蓝色边框,同时对象选择按钮将弹起组件板中的组件分为可视组件与非可视组件2.窗体设计器窗体设计器在创建新的项目时,窗体设计器是一个空白的窗口。可以用鼠标调整组件在窗体设计器上的位置和大小,还可以用对象观察器和代码编辑器来控制组件的外观和行为。3.对象观察器对象观察器利用对象观察器,可以修改窗体或组件的属性,或者使它们能够响应不同的事件。属性(Property)是一些数据,如高度、颜色、字体等,它们决定了组件在屏幕上的外观。事件(Event)则是一种消息处理机制,它能够捕捉某种情况的发生并做出反应,像鼠标单击和窗口打开就是两种典型的事件。对象观察器类包括Properties选项卡和Events选项卡下图两个对象观察器,左边一个按种类排序,右边一个按名字排序。注意:帮助系统是和对象观察器紧密结合在一起的,想了解某个属性或事件的帮助信息,只要在该属性或事件上按下F1键图1-5 按种类或名称查看Object Inspector4.代码编辑器代码编辑器代码编辑器是输入代码来指定应用程序行为的地方,也是Delphi根据应用程序中的组件自动生成代码的地方。当向应用程序中加入一个窗体时,Delphi会自动创建一个新的单元,并添加到代码编辑器顶部的标签中。5.代码浏览器代码浏览器 以一种树状视图方式显示列在代码编辑器中的单元文件。通过它,可以方便地在单元文件中漫游或在单元文件中加入新的元素或者把已有的文件改名。要记住代码浏览器和代码编辑器有一对一的关系。6.源代码生成器源代码生成器当对窗体设计器中可视化组件进行操作时DelphiIDE自动生成ObjectPascal源代码。当用【File】|【New】|【Application】菜单命令创建一个新的项目时,将看到屏幕上出现一个空白的窗体设计器,同时,代码编辑器中会自动出现一些代码,如下所示:unit Unit1;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs;typeTForm1= class(TForm) private Private declarations public Public declarations end;varForm1:TForm1;implementation$R*.dfmend.从上述源代码清单中可以看出,窗体对象是从TForm继承下来的。下面这一行非常重要:$R*.dfmPascal语言中的$R指令用于加载一个外部资源文件。*.dfm文件中包含在窗体设计器中创建的表单的二进制代码。其中的“*”表示与当前单元文件同名的文件。项目文件的扩展名是.dpr可以选择主菜单下的【Project】|【ViewSource】命令把项目源文件调入代码编辑器。如下用程序示例的项目文件:programProject1;usesForms,Unit1inUnit1.pasForm1;$R*.resbeginApplication.Initialize;Application.CreateForm(TForm1,Form1);Application.Run;end.当添加表单和单元时将出现在uses子句中1.2.2 基于组件的编程思想基于组件的编程思想.早期DOS操作系统和C语言主导的时代,“数据结构+算法”成为构建软件惟一方式.C+语言和面向对象技术.20世纪90年代中期流行一种崭新的程序设计概念:软件可以由可互换的组件构成组件是一种通过公开的属性、方法、事件,是可以重复使用的一种经过编译的二进制文件,其文件名可以是.OCX或者是.dll,如命令按钮、复选框、单选框、滚动条等都是常见的组件。1.2.3 Delphi 7的特点的特点 Delphi最显著的特点就是高效性和稳定性,主要体现在以下4个方面:可视化开发环境的性能。编译器的速度和已编译代码的效率。编程语言的功能及其复杂性。丰富的VCL1.可视化开发环境可视化开发环境可视化开发环境通常可分为3个部分:编辑器、调试窗口和窗体设计器。2.编译器的速度和已编译代码的效率编译器的速度和已编译代码的效率Pascal编译器最著名特点就是速度快,而Delphi正是建立在这种编译器基础之上的。增加了链接和各种缓存策略,尤其是在VisualC+和C+Builder中。3.编程语言的功能及其复杂性编程语言的功能及其复杂性 汇编是一种最有力的语言,即便是用汇编开发最简单的应用程序,难度也非常大,还可能一无所获。4.丰富的丰富的VCLVCL是Delphi7最重要的组成部分,包含不同种类的组件。1.2.4 帮助的使用帮助的使用(1)当遇到问题时,可以尝试按下【F1】键,一般情况下Delphi都会准确地定位。(2)如果需要浏览系统的一些帮助内容,比如说对象Pascal语言,可以通过菜单【Help】|【DelphiHelp】命令打开【帮助主题:DelphiHelp】对话框,在目录标签中展开相关条目,进行系统学习。3)当使用WindowsAPI函数时,通过【Help】|【DelphiHelp】打开的【帮助主题:DelphiHelp】对话框中,在【索引】选项卡中可能查不到有关的帮助信息,有两种解决办法:一种办法是在代码编辑器中输入WindowsAPI函数的名称,然后将光标定位到该名称中,接着按下【F1】键。另一种办法是通过【Help】|【WindowsSDK】打开帮助文件Win32.hlp,在【索引】选项卡中查找。(4)帮助文件打开时,要浏览相关内容,可以按下工具栏中的【】或【】按钮,通常这样是在同一个主题中浏览。通过一些SeeAlso热链接,也可以查看相关的帮助内容。(5)在组件栏上右击,通过快捷菜单的Help命令,可以查看有关组件的帮助内容。(6)在Delphi集成开发环境中,通过【Help】|【Customize】命令可以打开【OpenHelp】窗口,在该窗口中可以对帮助文件进行管理,其中包括帮助内容、帮助索引、链接位置和帮助工程文件等的管理。1.3 简单简单Delphi程序设计程序设计Delphi7编写应用程序包括新建应用程序、设置窗体属性、添加组件、设置组件属性、添加事件、编写事件响应代码、编译运行等1.新建应用程序新建应用程序启动Delphi7,选择【File】|【New】|【Application】菜单,新建一个应用程序。2.设置窗体属性设置窗体属性 单击【ObjectInspector】,在对象观察器中打开【Properties】选项卡,单击Caption属性右侧,输入窗体的新标题“窗口”。3.向窗体中添加组件向窗体中添加组件 单击Standard选项卡上的Button组件,将鼠标指向窗体中的任意位置(标题栏除外),单击鼠标,即可把Button1组件放入窗体中。或者直接双击组件面板上的Button组件,也可以在窗体中添加一个Button1组件。4.设置组件属性设置组件属性选中组件,单击ObjectInspector的标题栏以激活对象观察器,并选择Properties选项卡。在对象观察器中单击要设置的属性,进行属性设置。5.添加事件添加事件选中要添加事件的组件,激活对象观察器,并选择Event选项卡,在对象观察器中单击要添加的事件,在其右侧输入事件的响应函数名称,然后回车即可。6.编写事件响应代码编写事件响应代码选定要编写事件响应代码的组件,打开Event选项卡,双击要编写响应代码的事件右侧的空白部分,进入代码编辑窗口。procedureTForm1.Button1Click(Sender:TObject);/单击放大按钮,窗口将放大beginform1.Height:=form1.Height+10;/窗口高度10form1.Width:=form1.Width+10;/窗口宽度10end;procedure TForm1.Button2Click(Sender: TObject); /单击缩小按钮,窗口将缩小单击缩小按钮,窗口将缩小Beginform1.Height:=form1.Height-10; /窗口高度减窗口高度减10form1.Width:=form1.Width-10;/窗口宽度减10end;procedureTForm1.Button3Click(Sender:TObject);/关闭窗口beginclose;/退出end;7.编译运行程序编译运行程序图1-6 例程运行界面1.4 Delphi上机步骤上机步骤1.启动程序启动程序2.添加组件添加组件在窗体设计器中添加如图1-7所示的各组件。3.设置组件属性设置组件属性4.编写代码编写代码组件属性设置完成后,编写如下代码:图1-7 窗体布局图procedureTForm1.Edit1Change(Sender:TObject);/编辑框1中的内容改变时的事件varstringlength:integer;/定义整型变量,记录编辑框1中字符的个数beginstringlength:=edit1.GetTextLen;/得到编辑框1中字符个数edit2.Text:=inttostr(stringlength);/编辑框2显示编辑框1中字符个数end;procedureTForm1.Button1Click(Sender:TObject);/关闭窗口beginclose;end;5.保存工程保存工程保存工程分为保存单元文件和保存项目文件两步。第一步是保存单元文件,单击工具栏上的Save按钮,将打开SaveUnit1As窗口,默认的单元文件名是“Unit1.pas”,以“.pas”为扩展名,单元文件名根据需要可以另取。第二步是保存项目文件,单击工具栏上SaveProject1As按钮,默认的项目文件名是“Project1.dpr”,工程文件名根据需要可以另取以“.dpr”为扩展名6.运行工程运行工程保存工程结束后即可运行工程小结小结在本章中,主要介绍了Delphi的产生和发展,Delphi的特点,使用Delphi进行程序设计的一些基础知识。本章的重点是Delphi的开发环境和开发方法,通过实例介绍了Delphi的程序设计和上机操作的一般步骤。图1-10 工程运行界面图第第2章章 Delphi语法基础语法基础 2.1 保留字与标识符保留字与标识符2.1.1 标识符标识符标识符是ObjectPascal语言中各种成分的名称,这些成分包括变量(Var)、常量( Const) 、 类 型 ( Type) 、 过 程(Procedure)、函数(Function)、方法(Method)、单元(Unit)等。标识符可以分为三类:标准标识符、自定义标识符和限定标识符。1标准标识符(1)标准常量,如False、Maxint、True等;(2)标准类型,如Boolean、Char、Real等;(3)标准函数,如Sin、Cos、Abs、Arctan等;(4)标准过程,如Dispose、Get、New、Pack、Put等;(5)标准文件,如Input、Output等。2自定义标识符程序员根据程序设计的需要,自己定义的常量、变量、类型、函数、过程等所取的名字。自定义标识符可以由任意长的一个不带空格的字符串组成,包括字母AZ、az、数字09和下划线“_”等。定义标识符需要遵循以下规则:(1)标识符不区分大小写;(2)标识符只能以字母或下划线开头,不能以数字开头;(3)标识符可任意长度,但只有前225个字符有效;(4)标识符中间不允许有空格;(5)不允许使用ObjectPascal语言的保留字作为标识符。3限定标识符在Delphi程序中可引用多个单元,而各个单元中全局变量、函数、过程等可能会同名,在引用时需用限定标识符来区分它们:VarY:real;Y:=System.cos(pi);其中System称为限定符,而System.cos称为限定标识符。2.1.2 保留字保留字保留字由系统规定具有特定意义,不能被重新定义或作他用,定义了65个保留字。注意:1单词at和on具有特殊含义,不要与它们同名。2保留字和ObjectPascal一样不区分大小写。3Delphi集成开发环境的代码编辑器中,黑体显示保留字和指令字,定义时不要与这些黑体字一样。ObjectPascal的保留字andarrayasasmbegincaseclassconstconstructordestructordispinterfacedivdodowntoelseendexceptexportsfilefinalizationfinallyforfunctiongotoifimplementationininheritedinitializationinlineinterfaceislabellibrarymodnilnotobjectoforoutpackedprocedure programproperty raiserecordrepeatresourcestringsetshlshrstringthenthreadvartotrytypeunituntilusesvarwhilewithxor2.1.3 指令符指令符指令字只在特殊的程序位置、或当上下文关联时有意义的程序区段有自己特殊的意义,而在其他场合,用户可对其重新定义,即可将其定义为标识符,ObjectPascal不会指示出错,当用户重新定义这些指令字后,在作用域内它们就失去了原来的意义了。ObjectPascal中规定的指令符有39个说明:指令符private、protected、public、published和automated在定义对象类型时也作为保留字,而在其他场合则作为指令符。2.1.4 注释注释注释可增加程序的可读性和可维护性。ObjectPascal语言中注释有三种形式:1组合符号“”与“”的成对使用表示它们之间的内容为注释部分。2组合符号“(*”与“*)”的成对使用表示它们之间的内容为注释部分。3符号“/”的单个使用表示所在行的该符号之后的内容为注释。注意:1注释符“”与“”、“(*”与“*)”在使用时不支持注释的嵌套,而且必须成对使用。2对于单行和少量几行注释使用符号“/”,对于大块注释使用“”和“”或“(*”和“*)”。3有时可利用注释在代码中形成一个醒目标志。4在注释符“”或“(*”后紧接着是一个美元符号“$”时,表示该句是一个编译器指令,它与普通的注释不同,通常用来对编译过程进行设置。2.2 数据类型数据类型描述客观事物的数、字符以及所有能输入到计算机中并被计算机程序加工处理的符号的集合称为数据。数据类型可以分为标准数据类型及高级数据类型等,还可以通过数据类型声明语句在预定义数据类型的基础上定义新数据类型。说明:1标准数据类型属于ObjectPascal内部约定的数据类型,无需定义就可以直接使用。2高级数据类型体现了特殊的数据结构,在使用之前必须由用户自己定义。3数据类型中整型、字符型、布尔型、枚举型和子界型被称为顺序类型,其取值是一个有序集合,每一个可能取值都与顺序有关。2.2.1 数值型数据数值型数据数值型数据可分为整数类型和实数类型。1.整数类型整数类型是存储整数数据的类型,分为基本整形和一般整形。基本整形:短整型、小整型、长整型、64位整型、字节型、字型、长字型一般整形:整型、序数型注意:尽量使用一般整型Integer和Cardinal,可以最大限度发挥CPU和操作系统的性能。2.实数类型实数类型是存储实数数据的类型,分为基本实型和一般实型。基本实型:单精度实型、扩展型、双精度实型、货币型一般实型:实型注意:Real类型与Double类型完全等价。Currency类型至少有4位有效的小数位。2.2.2 字符型数据字符型数据ObjectPascal中的字符型数据可以分为字符型和字符串型2类7种。1.字符类型ObjectPascal包括3种形式的字符型数据类型类型名称名称字节数字节数取值范围取值范围Ansi字符型AnsiChar扩展ANSI字符集宽字符型WideCharUniCode字符集字符型Char()扩展ANSI字符集说明:(1)前2种为基本字符类型后一种为一般类型。(2)Char与AnsiChar完全等价,但Char常用。2.字符串类型字符串类型是存储字符串数据的类型,ObjectPascal包括了4种形式的字符串型数据类型类型名称名称最大长度最大长度所需内存空间所需内存空间短字符串型ShortString 255个字符2256B长字符串型AnsiString231个字符42GB宽字符串型WideString230个字符42GB字符串型String231个字符42GB说明:()AnsiString类型的定义是动态分配的,内容由AnsiChar类型的字符组成,长度仅受可用内存空间的限制,以空字符Nul作为结尾。()String字符串类型,既可以是ShortString类型也可以是AnsiString类型,默认定义是AnsiString类型。2.2.3 布尔型数据布尔型数据布尔型数据用于关系运算和条件语句的逻辑运算,包括4种形式的布尔型数据。说明:(1)后3种类型是为了兼容其他语言而设置的,编程时应尽量使用Boolean类型。(2)Boolean取值为False和True两个符号常量。类型类型名称名称字节数字节数取值取值布尔型Boolean只能为0(False)或1(True)字节布尔型ByteBool0(False)或非0(True)宽布尔型WordBool0(False)或非0(True)长布尔型LongBool0(False)或非0(True)2.3 常量与变量常量与变量2.3.1 常量常量常量即在程序的执行过程中其值不能改变的量。常量有两种,一种是常量值本身,也称为直接常量;另一种是要用声明定义的标识符表示的常量,也称为声明常量。声明常量又可以分为符号常量和类型常量。1.直接常量直接常量是指在程序中直接引用的常数,如整型常数、实型常数、字符型常数、字符串型常数和布尔型常数。2.声明常量(1)符号常量在程序中,某一个常数反复多次出现,可以定义一个标识符来代表该常数,这个标识符就是符号常量,其值在定义后不会改变。也称纯常量。定义符号常量使用常量说明语句,其语法格式为:Const=;=;其中Const是保留字,表示常量定义段开始。注意:保留字Const可单独一行也可与常量一行。不能在程序中给常量另行赋值,否则将导致语法错误。Delphi由常量值判断常量名属于哪种类型。(2)类型常量类型常量用于保存数组、记录、过程以及指针等类型的值,不能出现在常量表达式中。在默认的编译器状态下,类型常量的值可改变,但当在程序中加入编译命令$j-时,则类型常量的值在运行期就无法改变。声明类型常量的语法规则为:Const :=;其中类型是除文件型和可变型的所有类型,常量值可以是和类型相应的常量表达式。2.3.2 变量变量1.变量的声明变量在单元、函数或过程的声明部分进行声明,声明的位置决定了变量的作用域。声明包括两部分:变量名和它所属的类型,变量声明的语法格式为:Var:;:;当多个变量具有相同数据类型时,格式如下:Var,:;其中,Var是保留字,表示变量声明段的开始;同类型的可超过一个,间用“,”分隔;可以是基本数据类型或是由用户定义的高级数据类型;2.变量的使用一旦声明了一个变量应及时对它进行初始化,最简单方法就是给变量赋值,在表达式中使用变量。2.4 运算符与表达式运算符与表达式按照操作数数目的多少来分,运算符分为下面两类:单目运算符和双目运算符。单目运算符一般放在操作对象的前面,双目运算符都放在两个操作数之间。表达式是表示某个求值规则的运算公式,由运算符和配对的圆括号将常量、变量、函数、对象等操作数以合理的形式组合而成。2.4.1 算术运算符与算术表达式算术运算符与算术表达式1.算术运算符算术运算符对浮点数和整数进行加、减、乘、除和取模运算,取正“+”和取负“-”是单目运算符,其他均为双目运算符。说明:(1)+、-、*运算中,参加运算的数可以是整型和实型,结果自动向精度高的类型转化。(2)参加除法运算“/”,结果都是实型的商。(3)参加整数除法“Div”和求余运算“Mod”的数必须是整型,结果也是整型数,符号与被除数的符号相同,小数部分被舍去。(4)在表达式a/b、aDivb和aModb中,如果b的值为0,将会触发一个错误。2.算术运算符的优先级同级运算自左至右,如果含有括号,则先计算括号内表达式的值。3.算术表达式将数学式改写为算术表达式,考虑三个问题:一是语法,二是优先级,三是类型。优先顺序优先顺序运算符运算符+、-(取正、取负)*、/(法、除法)Div、Mod(整除、求余)+、(加法、减法)说明:(1)数学式中省略的运算符和表示函数参数的括号必须添加上去(2)必须注意优先级的处理,恰当利用标准函数,注意数据类型。2.4.2 逻辑运算符与布尔表达式逻辑运算符与布尔表达式逻辑运算符可分为布尔运算符、位运算符和关系运算符。1.布尔运算符只能对两个布尔型操作数进行运算,结果仍为布尔型,True或False。其中,NOT是求“非”,为一元运算符;AND是求“与”,OR是求“或”,XOR是求“异或”,均为二元运算符。2.位运算符运算符运算符 操作举例操作举例操作数类型操作数类型 结果类型结果类型 功能说明功能说明NOTNOTxintegerinteger即按二进制形式将每位求反ANDaANDbintegerinteger将两者相对应的位进行AND运算ORaORbintegerinteger将两者相对应的位进行OR运算XORaXORbintegerinteger将两者相对应的位进行取XOR运算,两者不同时结果为1SHLaSHLbintegerinteger将a的二进制值向左移动b位,左移一位相当于乘2SHRaSHRbintegerinteger将a的二进制向右移动b位,右移一位相当于除2注意:右移操作时原值的低位丢失,高位补0;左移操作时原值的高位丢失,低位补0。3.关系运算符关系符关系符操作操作操作数类型操作数类型结果类型结果类型=等于简单类型,字符串或可变类型,类,类引用,指针,集合类型Boolean不等于简单类型,字符串或可变类型,类,类引用,指针,集合类型Boolean大于简单类型,字符串或可变类型Boolean=大于等于 简单类型,字符串或可变类型Boolean4.布尔表达式布尔表达式由布尔运算符和布尔类型的操作数所组成,包括关系运算表达式和运算结果为布尔类型的函数,如Odd(x)、FileExists(x)、等。但位运算符的结果是整数类型,不能直接作为布尔操作数。2.4.3字符串运算符连接运算符“+”主要用于连接两个或更多的字符串。最简单的字符串表达式是字符常量、字符串常量、字符变量、字符串变量或字符函数的引用。字符串表达式格式为:+|当两个字符串用连接运算符连接起来后,第二个字符串直接添加到第一个字符串的尾部,结果是包含两个源字符串全部内容的新字符串。如果要把多个字符串连接起来,每两个字符串之间都要用“+”号分隔。2.4.4运算符的优先级优先顺序优先顺序 运算符运算符分类描述分类描述1(取地址),NOT,-一元运算符2*,/,DIV,MOD,AND,SHL,SHR 乘除及类型强制转换运算符3+,OR,XOR加减运算符4=,=,in,is关系、集合成员及类型比较运算符2.5常用系统函数与过程常用系统函数与过程2.5.1数值运算函数Delphi的数值运算函数包含了常用的数学函数(如三角函数、对数函数等)和适合计算机数据处理的其他函数(如求数组中的最大值、求三角形的斜边长等)。2.5.2字符处理函数对字符的处理主要包括:大小写转换、比较先后顺序、合并、查找、截取、插入、求长度以及类型转换等。2.5.3日期时间函数调用日期时间函数可对日期和时间进行处理2.5.4顺序类型函数1.顺序类型顺序类型指整型、字符型、布尔型、枚举型、子界型5种数据类型,如下所述:(1)整数的的序数是其自身;(2)字符的序数是其ASCII码;(3)布尔型数据:False序数为0,True为1;(4)枚举型第一个数据序数为0,其余类推;(5)子界型第一个数据序数为1,其余类推。除第一个序数,每一个都有一个前趋值;除最后一个序数,每一个都有一个后继值。2.顺序函数2.6语句语句2.6.1 语句的基本概念语句的基本概念按执行时间可分为:声明语句和可执行语句。顺序函数顺序函数引用形式引用形式函数功能描述函数功能描述序数函数Ord(x);返回数据x的序数前趋函数Pred(x);返回数据x的前趋值。如果将Pred函数用于第一个数据,就可能产生一个编译时的错误后继函数Succ(x); 返回数据x的后继值。如果将Succ函数用于最后一个数据,就可能产生一个编译时的错误首序数函数 Low(x);返回顺序型数据x取值集合中的第一个值(序数最小)。它还可以返回数组的第一个元素末序数函数 High(x);返回顺序型数据x取值集合中的最末一个值(序数最大),它还可以返回数组的最末一个元素声明语句包括单元说明语句、类型说明语句、变量说明语句、过程说明语句、函数说明语句和程序区段标识语句等。可执行语句包括赋值语句、运行控制语句和结构控制语句等。按语句的描述形式,可分为简单语句、结构语句和复合语句等。简单语句只含有一个语句定义符或特殊标志;结构语句往往含有多于一个的语句动词;复合语句则是由begin和end括起来的若干个简单语句、结构语句和复合语句,允许复合语句多层嵌套,或为空,也就是在begin和end之间没有其他语句。2.6.2 常见声明语句常见声明语句1.标号声明语句标号声明语句即用一个整型数来表示程序的某个执行语句,一行标号声明语句可以同时声明几个标号,其用法如下所示:label Aa,Ab;varI:integer;begin/语句if(I=0)then gotoAa;/语句Aa:begin/语句end;end;2.类型声明语句在ObjectPascal中,所有的变量必须是某种特定的数据类型,类型决定了它所能包含的数值和可进行的操作,用类型声明语句可以定义新的数据类型。例如:TypeTmyDim:Array1.10,1.5ofDouble;3.过程声明语句过程可以被看成一段小程序,用来实现某种特定的目标,在完整的程序中它被当作一个语句来执行。在建立过程之前应先声明。procedureNumString(N:Integr;VarS:string);4.函数声明语句函数与过程相似,主要区别在于函数必须有返回值,函数的声明参见下面的语句,其中,最后的Real表示函数的返回数据类型。FunctionPower(X:Real;Y:Integer):Real;2.6.3 赋值语句和程序的顺序结构赋值语句和程序的顺序结构1.赋值语句赋值语句的语法格式为::=;2.类型兼容类型兼容是指数据类型不完全相同的量之间能进行的运算和赋值操作。3.利用赋值语句给对象属性赋值由于属性总是归属于对象才有实际意义,所以引用属性时用符号“.”来连接表示其隶属关系。如组件Editl的字体的颜色属性表示为:Editl.Font.Color。4.顺序结构顺序结构是最简单、最常用的结构。在该结构中,各操作块按照出现的先后顺序依次执行,不产生程序流程的其他转移。它是任何程序的主体结构,即使在选择结构或循环结构中,也常以顺序结构作为其子结构。通常由若干个赋值语句或其他简单语句构成。2.6.4 条件语句和程序的选择结构条件语句和程序的选择结构实现选择结构的是IF语句和Case语句,这两种语句又称条件语句,条件语句的功能就是根据表达式的值有选择地执行一组语句。1.if语句通过条件的布尔表达式值选择执行路径。ifthenelse;if语句分为简单条件语句和复合条件语句。简单条件语句的中不包含其他的条件语句。如果在If语句格式中的或本身又是一个If语句,则称为If语句的嵌套,嵌套的If语句又被称为复合条件语句。2.Case语句Case语句用来实现多分支选择结构。Case语句描述了多路择一的功能,它根据“选择器表达式”的值决定执行相应的语句。Case语句的语法格式为:caseof:;:;else;end;说明:的值必须是顺序类型。2.6.5 循环语句和程序的循环结构循环语句和程序的循环结构从某处开始有规律地反复执行某一程序块的现象称为“循环”,完成这一功能的程序结构为“循环结构,”而其中重复执行的程序块称为“循环体”。循环结构语句有3种,它们分别是:While语句、Repeat语句以及For语句。1.While语句While语句属于前测型循环结构。首先判断条件,根据条件决定是否执行循环,执行循环的最少次数为0。其语法格式为:whiledo;说明:可在(循环体)中任何位置Break语句来终止While循环,Break语句通常位于IF语句后。可在循环体中任何位置放置Continue语句,以便在整个循环体没有执行完就重新判断(条件),以决定是否开始新的循环。Continue语句通常位于IF语句之后。2.Repeat语句Repeat语句属于后测型循环结构,首先执行循环体,然后判断条件,根据条件决定是否继续执行循环,执行循环的最少次数为1。repeatuntil ;3.For语句若知道要执行多少次循环时,则使用For循环结构。For循环使用一个循环变量,每重复一次循环之后,循环变量的值就会自动增加或者减少。For语句的语法格式为:for=to|downtodo;说明:只能是顺序类型,TO表示计数器递增,DownTo表示计数器递减。4.循环的嵌套循环语句的循环体中仅包含了简单语句,称为单重循环。如果在循环体中又包含了另一个循环结构,则称为多重循环,又称为循环的嵌套。在循环体中的嵌套称为内循环,外部的循环称为外循环。多重循环嵌套根据循环结构嵌套层数可以分为二重循环、三重循环等。5.循环的中断特殊情况下,需要中断正在执行的循环,可以使用break语句或Continue语句。可以放在循环体的任意位置,通常放在If语句之后。执行Break语句的结果是:跳出整个循环,执行之后的语句。执行Continue语句的结果是:跳出本轮循环,然后判断循环条件是否成立,再决定是否开始新一轮的循环。小结小结本章主要介绍了ObjectPascal的最基本的语法,主要包括基本词法、基本数据类型、常量与变量、运算符与表达式、常用函数与过程、语句等。第3章常用组件Delphi7.0的组件板上含有27个选项卡,总共包括350多个组件,如图所示:3.1 窗体窗体3.1.1 Form组件组件窗体是应用程序的操作界面,是放置组件的基础。窗体由标题栏、工作区和边界组成。图3-1 组件板和选项卡运行界面图1.Form的主要属性2.窗体组件(TForm)在运行时表现为一个窗体,窗体是一个容器构件,它可以包含其他种类的构件,并协同完成应用程序的整体功能。窗体由属性、事件和方法组成。(1)BorderIcons属性用来制定窗体标题栏上的图标(2)BorderStyle属性Borderstyle属性用来设置窗体的外观和边框(3)Name属性Name属性唯一地标识对象,取值不能为空,若工程中有多个窗体,名称不能相同。(4)Caption属性用来指定窗体标题栏中说明文字,可以为空。(5)Font属性Font属性用来设置窗体中文字的字体、颜色和字号等等,其中Font.style属性为集合型。(6)FormStyle属性FormStyle属性用来指定窗体的类型。从窗体类型的角度来看,Windows环境中的应用程序可以分为以下三类。第一类:多文档界面(MDI)应用程序一般这种应用程序具有一个父级窗口和多个子窗口,可以同时打开多个文档,分别在多个子窗口中显示。第二类:单文档界面(SDI)应用程序这种应用程序同时只能打开一个文档。第三类:对话框应用程序这种应用程序的主界面基于一个对话框类型的窗体。(7)Icon属性Icon属性用来指定标题栏中显示的图标。(8)Position属性Position属性用来描述窗体大小和显示位置。(9)WindowsState属性WindowsState属性来描述窗体显示状态。2.TForm的事件窗体是一个可视化的组件,包括外部事件和内部事件。3.窗体的方法一些常用方法(过程或函数)有:Create、Close、CloseQuery、release、Show、ShowModal、Print。4窗体的创建创建窗体的方法分为两种:静态创建和动态创建。所谓静态创建窗体是指再工程的编辑、设计时创建新窗体;而动态创建窗体是指在工程的运行时通过代码生成窗体。(1)静态创建新窗体通过集成开发环境中的【File】|【New】|【Application】菜单,创建一个应用程序,此时自动生成一个窗体Form1,再打开【File】|【New】|【Form】菜单生成一个窗体Form2。在Form1中添加两个Button、1个Label组件,Form2中添加1个Label组件,即可完成界面设计。添加代码:procedureTForm1.Button1Click(Sender:TObject);/创建按钮事件begin /关键分析form2.show;/调用Show方法显示Form2窗体end;procedureTForm1.Button2Click(Sender:TObject);beginform1.Close;end;程序分析:编译上述工程时,系统会弹出出错提示信息,单击“Yes”按钮,Delphi将自动在Unit1单元中添加对Unit2单元的引用。(2)动态态创建新窗体在需要某个窗体时,临时创建它,使用后将其立即释放,这种称为窗体的动态创建。3.1.2弹出对话框窗体弹出对话框窗体Delphi提供内部对话框有两种。第一种:信息输出对话框Showmessage过程、ShowMessageFmt过程、MessageDlg函数、MessageDlgPos函数、CreateMessageDialog函数。第二种:信息输入对话框InputBox函数、InputQuery函数。1ShowMessage过程其语法格式为:ShowMessage();2ShowMessageFmt过程的语法格式为:ShowMessageFmt(,);3MessageDlg函数函数其语法格式为:其语法格式为:=MessageDlg(,HelpCtx);4MessageDlgPos函数其语法格式为:=MessageDlgPos(,HelpCtx,X,Y);可以指定对话框的显示位置坐标:X,Y。5CreatMessageDialog函数其语法格式为:=CreatMessageDialog (,);6InputBox函数函数其语法格为:其语法格为:=InputBox (,);7InputQuery函数其语法格式为:=InputQuery (,);3.2 输入显示类组件输入显示类组件3.2.1Edit组件编辑框(Edit)是一种通用组件,既可以输入文本,又可以显示文本,编辑框组件位于Standard组件板中。图3-7编辑框Edit 运行界面图1Edit的主要属性(1)AutoSelect属性:设置编辑框得到焦点时,文本是否自动被选中。(2)AutoSize属性:决定编辑框是否自动随字体的变化而改变大小。(3)Enable属性:用来设置编辑框是否能用。(4)BorderSytle属性:设置编辑框边框类型。(5)MaxLength属性:设所能接受最大字符数。(6)PasswordChar属性:设置非#0字符时,将代替用户输入的字符被显示。(7)ReadOnly属性:定编辑框中的文本是否可以编辑。(8)SelStart属性:被选中文本的开始位置,或光标在文本中的位置。(9)SelText属性:被选中的文本。(10)SelLength属性:被选中文本的长度。(11)Text属性:编辑框中的文本内容。(12)CharCase属性:控制编辑框中文本大小写3.2.2Label组件标签组件位于Standard组件板中。图3-8 标签Label 运行界面图1Label的主要属性(1)Caption属性:用来显示标签的文本。(2)ShowAccelChar属性:决定是否将&作为作为热键字符的标记。(3)AutoSize属性:决定标签是否自动随文本的变化而改变大小。(4)Alignment属性:决定对齐方式。(5)Layout属性:控制文本显示在标签的位置。(6)WordWrap属性:控制是否折行显示。(7)Transparent属性:决定背景是否透明。(8)FocusControl属性:用来获得焦点组件名。3.2.3 Memo组件组件备注框组件位于Standard组件板中。1Memo的主要属性备注框在Delphi中用Tmemo类处理,Tmemo类是Tedit类的衍生类,为了处理多行文本,Tmemo类还增加了一些新的属性。(1)CaretPos属性:得到光标在编辑区中位置。(2)Lines属性:用来存放Memo对象的文本。(3)Modified属性:确定文本是否被改动过。图3-10 备注框 Memo运行界面图(4)ScrollBars属性:决定备注框是否具有滚动条。(5)WordWrap属性:设置文本是否能够换行。(6)WantReturns属性:用来设置备注框是否能插入“回车”键。(7)WantTabs属性:用来设置备注框是否能插入“Tab”键。2Memo的使用【例3.4】利用编辑框,把编辑框中的文本输入到Memo中。(1)界面设计创建一个新的工程,在窗体中添加1个按钮Button1组件、一个编辑框Edit1组件和一个备注框Memo1组件,各组件的属性设置如图:(2)程序设计procedureTForm1.Button1Click(Sender:TObject);beginMemo1.Lines.Add(Edit1.Text);edit1.Text:=;edit1.SetFocus;end;图3-12 Memo示例运行界面图procedureTForm1.FormActivate(Sender:TObject);beginedit1.SetFocus;end;3.2.4 MaskEdit 组件组件它限制用户在所定义的位置输入要求输入的符号。掩码编辑框(MaskEdit)组件位于Additional附加组件板”中。1MaskEdit常用属性图3-13 Memo示例运行界面图()EditMask属性EditMask属性用来控制用户输入数据格式的掩码字符串,掩码字符串EditMask属性分为三个部分,用分号分隔。第一部分是掩码字符串的主要部分,它确定了数据的格式;第二部分决定是否将掩码中的字符串作为数据的一部分,0表示不作为数据的一部分,1表示作为数据的一部分,它将影响属性;第三部分指出在掩码中用来代表未输入数据的字符。(2)EditText属性:用来返回用户输入的数据。3.3按钮类组件按钮类组件3.3.1 Button组件组件Button按钮在Delphi7.0组件板Standard选项卡中1.Button的主要属性(1)Caption属性:来制定按钮所显示的文字。(2)Cancel属性:决定改按钮是否为取消按钮,缺省值为False。(3)Default属性:用来决定改按钮是否为默认按钮,缺省值为False。图3-16 Button基本按钮运行界面图(4)ModalResult属性:用来决定模式窗体如何被关闭。2.Button的事件Button组件常用的事件如表所示。在下述两种情况下,OnClick事件将被激发:(1)用鼠标单击按钮。(2)按钮获得焦点时按下键或空格键。事件事件含义含义OnClick鼠标单击事件OnMouseDown鼠标按下事件OnMouseMove鼠标移过事件OnMouseUp鼠标释放事件3.3.2 BitBtn组件组件位于Delphi7.0组件板Additional选项卡中。1BitBtn的主要属性(1)Glyph属性:为bitBtn制定一个.bmp文件,显示再按钮的表面。(2)Kind属性:决定bitBtn按钮的种类。(3)Layout属性:用来控制bitBtn按钮中位图与文本的相对位置。默认值为blGlyphLeft。(4)Margin属性:用来控制bitBtn按钮中位图与边界之间的象素个数。图3-17 BitBtn按钮运行界面图(5)Spacing属性:用来控制bitBtn按钮中位图与文本之间的(距离)象素个数,默认值为4。2.BitBtn的事件BitBtn组件常用的事件如表所示。在下述两种情况下,OnClick事件将被激发:(1)用鼠标单击按钮。(2)按钮获得焦点时按下键或空格键。事件含义OnClick鼠标单击事件OnMouseDown鼠标按下事件OnMouseMove鼠标移过事件OnMouseUp鼠标释放事件3.3.3 SpeedButton组件组件快速按钮(SpeedButton)是一种可以成组工作的按钮,具有将位图显示在按钮表面的功能;还具有允许其中一个按钮被选中(按下)的功能;当它单独使用时具有开关的功能。快速按钮位于Additonal组件板中。SpeedButton的主要属性有:(1)AllowAllUp属性:控制是否允许单击处于按下状态的按钮,使之恢复到松开状态。默认值为False。图3-19快速按钮SpeedButton运行界面图(2)Down属性:设置按钮是否处于按下状态。(3)Flat属性:当取值为True时,按钮具有Office97工具栏的风格。默认值为False。(4)GroupIdex属性:该属性默认值为0,表示不与其他SpeedButton成组。3.4复选框、单选按钮和单选按钮组复选框、单选按钮和单选按钮组3.4.1CheckBox组件位于Delphi7.0组件板Standard选项卡中。图3-21 复选框CheckBox复选框CheckBox具有选中和未被选中两种状态,未选中状态,选中状态。还有一种不确定状态,表示既非选中又非未选中。1CheckBox的主要属性(1)Checked属性:用于表明CheckBox是否被选中。(2)State属性:属性State进一步确定CheckBox状态。有3种值:cbChecked、cbUnchecked和cbGrayed,分别为选中、未选中和不确定。(3)AllowGrayed属性:为True时,复选框有3种选择:为False时,只有选中和未选中状态。3.4.2RadioButton组件位于Delphi7.0组件板Standard选项卡中。RadioButton的主要属性有:Checked属性:表明CheckBox是否被选中。RadioButton有两种状态,如果当Checked属性为True时,表示选中状态,如果当Checked属性为False时,表示未选中状态。3.4.3RadioGroup组件位于Delphi7.0组件板Standard选项卡中。图3-23单选按钮RadioButton1RadioGroup的主要属性(1)Columns属性属性Columns用于设置单选按钮组中按钮的列数。范围116,默认值为1。(2)Items属性:用于设置各种单选按钮标题。(3)ItemIndex属性:单选按钮组中被选中按钮(从0开始)的序号。默认值为-1,表示组中按钮均未被选中。图3-25单选按钮组RadioGroup 3.5列表框、组合框列表框、组合框3.5.1ListBox组件当列表框不能同时显示所有选择项时,将自动加上一个垂直滚动条,使用户可以上下滚动列表框,以查阅所有的选项。列表框位于组件板Standard选项卡中。ListBox的主要属性:(1)Items属性:列表框中选项的集合。(2)ItemsIndex属性:为选项的索引值。图3-28 列表框 ListBox运行界面图(3)Stored属性:决定选项是否排序。(4)Columns属性:决定列表框的列数。(5)MultiSelect属性:定是否可以选择多项。(6)SelCount属性:被选中的项的数目,只读。(7)Selected属性:设置或返回是否被选中。(8)IntegralHelght属性:True自动调整框的高度使每行的高度(IntemHeight)可以完整地被显示。False不自动调整框的高度,非完整高度行被显示在框的底部。(9)ItemHeight属性:控制列表框中行的高度。(10)Style属性lbStandard固定Font.Size属性与ItemHeight属性之比。lbOwnerDrawFixed可以调整ItemHeight,并将自动调整框的高度以适应行高。LbOwnerDrawVariable可以调整ItemHeight属性,需手动调整框的高度以适应行高。3.5.2ComboBox组件兼有EditBox和ListBox两者功能,用户可以通过键入文本或选择列表中项目来进行选择。组合框位于组件板Standard选项卡中。1.组合框的主要属性(1)Items属性:列表框中选项的集合。(2)ItemsIndex属性:为选项的索引值。(3)Stored属性:决定选项是否排序。(4)DorpDownCount属性:控制组合框下拉列表所能显示选项的最大个数。(5)SelText属性:存储显示于编辑区中被选中项的内容。(6)Style属性:决定组合框的风格。图3-29组合框 ComboBox3.6 滚动条滚动条要想自己操纵窗口的滚动,就要用到TScrollBar组件。当在滚动条上操作时,将触发OnScroll事件,TScrollBar组件直接继承于TwinControl中,位于Standard选项卡中。1ScrollBar主要属性、方法与事件(1)LargeChange属性:当用户单击滚动条时,滚动距离由LargeChange属性设置,默认1。(2)Max、Min属性:设置滚动条可滚动的范围图3-31 滚动条 Scrollbar(3)PageSize属性:当用户按键盘上的PageUp或PageDown键时,滚动条滚动的距离是由PageSize属性设置的,默认是1。(4)Position属性:设置或返回滚动条中小方块的位置。(5)SmallChange属性:是用户按滚动条两端的箭头时滚动条的距离,默认值是1。(6)SetPaxams方法:该过程相当于分别设置Position、Max和Min属性。(7)OnScroll事件:第三个参数返回滚动条小方块的位置,第二个参数返回滚动条的状态。3.6计时器计时器Timer组件位于System组件板中,如图所示,属于非可视化组件,在设计时显示为一个小时钟图标,而在运行时则不可见了,冲用来做一些后台处理。1Timer组件的主要属性与事件(1)Enanled属性:为Ture时,定时器开始工作,为False时定时器暂停工作。(2)Interval属性:用来设置定时器触发周期。图3-32 Timer计时器(3)OnTimer事件:Timer组件只提供一个事件,即OnTimer。该事件以Interval属性设置的频率被触发。3.7对话框组件对话框组件3.7.1Opendialog组件用于打开一个已经存在的文件,用户选择某一文件,其所在的驱动器、文件夹、文件名以及扩展名将被赋予Opendialog的filename属性。Opendialog组件位于Dialogs组件板如图3-34的所示的第一个组件。图3-34Dialogs组件板Opendialog组件的主要属性(1)DefaultExt属性:用于设置系统自动附加的扩展文件名,既在用户没有设置文件类型时系统会自动附加该文件类型。(2)Filter属性:设置可打开的文件类型。Filter属性的设置可点击右端按钮,打开如图所示的对话框进行设置。图3-35Filter Editor对话框(3)FilterIndex属性:设置默认的Filter值,为1时则默认的文件类型为Filter属性中列举的第一个文件类型。(4)Initialdir属性:对话框打开的初始化路径。(5)Options属性:设置对话框的作用及表现形式。包括是否可选择多个文件、是否允许长文件名、是否可以调节对话框的大小等。3.7.2Savedialog组件用于提供一个另存为对话框,用户输入某一文件,其所在的驱动器、文件夹、文件名以及文件扩展名将被赋予SaveDialog的filename属性。Savedialog组件位于Dialogs组件板如图3-34的所示的第二个组件。3.7.3Fontdialog组件用于提供一个字体对话框,用户可以选择需要的字体名称、样式、大小、效果及字体颜色等,这些选择将被赋予Fontdialog的Font属性。Fontdialog组件位于Dialogs组件板如图3-34的所示的第五个组件。3.7.4Colordialog组件用于提供一个颜色对话框,用户可以选择需要的颜色等属性,这些选择将被赋予Colordialog的Color属性。Colordialog组件位于Dialogs组件板如图3-34所示第六个组件。3.8 Win3.1组件组件3.8.1FileListBox组件用于显示指定目录文件名滚动列表,位于如图3-38所示Win3.1组件板中第八个组件。FileListBox组件的主要属性:(1)Directory属性:设置当前文件目录,显示的文件列及表自动更新显示文件目录的文件。(2)Drive属性:用于设置当前驱动器盘的号,当前属性值改变时,Directory属性值自动改变为新的驱动器下的当前目录。图3-38 Win3.1组件板(3)ExtenderdSelect属性:若为Ture则可按着键然后用鼠标选择多个文件。(4)FileEdit属性:用于将文件列表链接至一个编辑组件,显示列表中当前被选中的文件。(5)FileName属性:存放了列表中当前被选中的文件的文件名及路径名。(6)FileType属性:决定了文件列表中显示的文件的属性类型。(7)Mask属性:用于设置文件列表中显示的文件类型。(8)ShowGlyphs属性:用于设置文件是否在文件旁边显示文件图标。(9)MultisSelect属性:用于设置用户是否可以一次选中多个文件。3.8.2DirectoryListBox组件用于显示指定驱动器下的目录列表,该组件位于如图3-38所示中第九个组件。DirectoryListBox组件的主要属性:(1)Directory属性:用于设置当前的文件目录。(2)DirLabel属性:用于将目录列表链接至一个lable组件,显示列表中当前被选中目录。(3)Drive属性:用于设置当前的驱动器盘号,当该属性值改变时,Drive属性值将自动改变为新的驱动器下的当前目录。(4)FileList属性:用于将目录列表链接至文件列表,当目录列表中的目录改变时,文件列表会自动进行更新。3.8.3DriveComboBox组件用于显示一可选驱动器下拉列表,该组件位于如图3-38所示中第十个组件。DriveComboBox组件主要的属性:(1)Dirlist属性:用于将本组件链接至目录列表,如驱动器改变,目录列表会自动更新。(2)Drive属性:用于存放当前的驱动器盘号。(3)TextCase属性:用于决定驱动器盘号使用大写字母还是小写字母。3.8.4FilterComboBox组件用于显示一可选过滤器下拉列表,供用户选择,位于如图3-38所示中第十一个组件。FilterComboBox组件的主要属性如下:(1)FileList属性:用于将本组件链接至文件列表,如当前的文件类型改变,文件列表会自动进行更新。(2)Filer属性:用于设置各种过滤文件的类型。(3)Mask属性:用于存放所选的过滤类型的对应。3.9 菜单菜单一个Windows引用程序,它往往需要制作标准的菜单界面,包括主菜单Mainmenu、弹出式菜单Popmenu两种。3.9.1MainMenu组件主菜单也称为菜单栏,其中包括一个或多个选择项称为菜单项。当单击一个菜单项时,包含子菜单项的列表即被打开。主菜单位于组件板Standard选项卡中。如图所示:图3-40主菜单 MainMenu1菜单编辑器打开一个新的窗体,在其中添加一个MainMenu组件,即可产生主菜单项,菜单项的设置可以通过双击MainMenu组件或右键单击MainMenu组件,在弹出的快捷菜单中选取Menudesigner项,或者选择MainMenu组件Items属性,单击右端按钮,可打开菜单项编辑器,并产生一个空菜单项,如图所示。图3-41菜单项编辑器载对象编辑器中,Caption属性输入“&S设置”,表示可按钮【Ctrl】+【S】或【Alt】+【S】键来选择此菜单项,其中“&”符号后的第一个字符为加速字符。若输入“-”则表示建立菜单分割线将菜单项分组。按【Insert】键则插入一个新的空白菜单项,若按【Delete】键则删除一个菜单项。在Delphi中建立子菜单时,可选择要产生子菜单的菜单项,然后按【Ctrl】+【】键,便产生下一级子菜单项。2MenuItem的主要属性(1)Name属性:用于设置菜单组件的名称。(2)Caption属性:显示菜单组件的标题。(3)Checked属性:设置为True时,相应的在菜单项边上加上选择标志“”。属性设置为False时,则无显示,默认值为False。(4)Enabled属性:默认值为True,表示可以响应用户事件,若设置为False,则无法相应用户事件,并且相应的菜单项会变灰。(5)Vsible属性:确定菜单项是否显示,True则显示,False则隐藏。(6)ShortCut属性:设置该菜单项的热键。3.9.2PopupMenu组件应用系统中,对弹出式菜单的支持是一种流行的方式,就是我们通常使用的右键菜单,当用户在不同的地方点击鼠标右键,就弹出不同的菜单项。主菜单位于组件板Standard选项卡中,如图3-43所示。弹出式菜单的设计方法基本和主菜单设计方法相同,如果需要在某个组件上鼠标右键点击打开一个弹出式菜单,那么只需要将此组件的PopupMenu属性设置成需要打开的弹出式菜单的名字即可。图3-43 弹出式菜单PopupMenu第第4章章 过程与函数过程与函数一个比较大的程序可以被划分成若干个模块,每个模块完成一个或几个功能,每个功能可以用一个程序段来实现,这个程序段被称为“子程序”。在Delphi中,过程(Procedure)指没有返回值的“子程序”,而函数(Function)是有返回值的“子程序”。4.1 过程过程运行结束后没有返回值的子程序称为过程。在Delphi7中有三种类型的过程:即标准过程、事件过程和自定义过程。其中标准过程和自定义过程又可以称为通用过程。即这两种过程可以独立于事件,被任何过程或函数调用。4.1.1标准过程标准过程是系统内部已经定义好的过程,不需要编写代码,也不能改变过程的名称和参数。标准过程的调用非常简单,在需要调用的位置直接书写该过程即可。4.1.2事件过程当对象接受到某个动作时,Windows会通知Delphi产生一个事件(鼠标单击事件),而Delphi会自动执行该对象与该事件有关的一段程序,这就是该对象的一个事件过程。1事件过程的创建在窗体上(或在对象监视器中)选中该对象,然后在对象监视器的事件(Event)选项卡中选择相应的事件名,用鼠标双击其右侧的下拉列表框,Delphi将自动产生一个默认的事件过程框架,执行该事件的代码需要添加在框架内。Delphi产生的默认事件过程的名称遵循下面的命名原则:控件名称加上事件类型名(无On)。2事件过程调用已经创建完成的事件过程可以被其他事件过程调用。4.1.3自定义过程一般自定义过程定义在单元的implementation部分的中$R*.dfm后面。1自定义过程声明自定义过程的一般声明格式如下:procedure ( ) ;局部声明局部声明begin end;过程的声明必须以procedure开始,包括过程名,形参表,局部声明部分和以begin开始end结束的过程语句序列。第一行必须以“;”分号结束,是过程首部。其余部分为过程的实现部分,必须包括一个begin-end结构。End后面也必须以“;”分号结束,表示过程结束。过程参数的一般书写形式为:(Var|Const:)Var和Const为系统的保留字,Var表示参数传递方式为地址传递,即形参值的改变将反映到实参中。Const表示在过程内部不能改变形参的值。不带这两个参数的形参传递方式为值传递,即过程内部对形参值的改变将不会反映到实参中。过程的定义可以有类似下面的两种形式:procedureGetSum(V1,V2:Integer;varSum:Int64);procedureProcNoPara;2自定义过程的创建和使用一般可以创建两种自定义过程。一种是仅能本单元中使用,对其他的单元文件该过程不可见;另外一种就是还可以在其他单元中使用的公共过程。要创建能在其他单元中使用的过程,必须将过程首部声明在单元的公共接口部分(Interface)中。如果是仅在本单元内部使用的自定义过程,则必须先创建才能被使用。4.2 函数函数函数是有返回值的子程序,一般通过函数名或一个系统预定义的隐含变量Result返回函数的值。在Delphi中有两种函数,内部函数和自定义函数。4.2.1标准函数标准函数是系统内部已经定义好的函数。不能改变标准函数的参数以及返回值类型。其定义形式如下:function StrToInt(const S: string): Integer;表明该函数接受一个常量参数,并返回一个整型值。4.2.2自定义函数1函数的定义一般的语法格式为:Function ():返回类型;返回类型;局部声明局部声明BeginEnd ;自定义函数含有一个以Function开始的函数首部,包括函数名,函数的形参表和函数的返回值类型以及返回值类型前面的“:”冒号和后面的“;”分号。一个函数可以没有形参表,但必须有函数返回值类型,同样也可以没有局部声明,但必须有一个实现函数功能的函数体,以Begin开始,End结束。在End后也必须有一个“;”分号表示函数体的结束。2自定义函数的创建和使用对于仅在本过程内部使用的函数,必须遵循先创建在使用的原则。如果想让一个函数对其他的单元也是可见的,则必须将函数首部定义在单元的接口部分。则函数功能的实现和调用之间的位置就不必遵循先创建再使用的规则。同样也需要使用一个与函数的返回值类型相同的变量来接受函数的返回值。4.3 内部过程和函数内部过程和函数内部函数和过程是指定义在一个过程和函数内部,只能由该过程和函数使用的函数和过程,内部程序又称为程序嵌套。1嵌套层次为了准确地表达嵌套层次,通常将嵌套从外向内进行编号,并把相应子程序的层号称为子程序的嵌套深度。一般单元文件为0层,其中的子程序从外向内依次为1层、2层、3层、。Unit 0层A1 1层A2 2层A3 3层图4-3 嵌套层次关系图运行界面图有嵌套关系的子程序,若层号相差为1,称为相邻层,并称层号小的为外层子程序,层号大的为内层子程序;若层号相差大于1,则称为隔层。子程序的嵌套要求外层子程序能够完全包含内层子程序,不允许局部包含,即不允许交叉。2子程序的调用规则Delphi中,子程序调用必须遵循如下规则:(1)子程序可以调用其相邻内层的子程序,不能隔层调用。如图4-4所示,A1可以调用A1B和A1C但不能调用A3。(2)内层子程序可以调用外层的子程序而且允许隔层调用。如A2B可以调用A2,A3调用A1(3)同一层的子程序,允许后定义的子程序调用先定义的子程序,如A2C可以调用A2B但是A2B不可以调用A2C。(4)如果需要调用同层中后定义的子程序,必须用保留字forward(超前引用)对后面的子程序提前说明。Unit 0层A1 1层A1B 2层A3 3层图4-4 子程序的调用规则A1C 2层A2 1层A2B 2层A2C 2层4.4 参数的传递参数的传递4.4.1形式参数与实际参数形式参数是指出现在过程或者函数首部“形参表”中的变量名,表示用于接收数据的变量。实际参数是指在调用过程或是函数时,传递给过程或函数的常量、变量或表达式。在过程或是函数的定义中,使用形式参数来确定该过程或函数所需要的参数的个数、类型以及参数之间的次序。在调用该过程或是函数时,实际参数将替换形式参数,形参和实参之间的对应关系为:第一个形参接受第一个实参的值,第二个形参接受第二个实参的值,依次类推。4.4.2参数的传递方式在Delphi中,有两种参数的传递方式,“按值传递”和“按地址传递”。在过程或者函数的首部“形参表”中的参数前面使用系统的保留字Var或者Out的形参变量表示为“按地址传递”,使用Const或没有任何保留字的形式参数,将使用“按值传递”的方式。声明为“按值传递”的参数仅负责得到实际参数的值,不保留内部对该参数的改变,而声明为“按地址传递”的参数将保留函数或过程内部对实际参数值的改变,并在调用结束后返回该值。“按地址传递”的参数实参和形参的类型必须一致,而“按值传递”的实参和形参之间仅需要赋值相容即可。4.4.3使用缺省参数在声明函数或过程中,可以给形参指定一个缺省的值,在调用时,如果没有给形参指定实参,则系统自动使用缺省的值,如果赋值,则使用实际参数的值。缺省参数声明的方法是在形参的类型后面使用“=”等号,并给出具体的常量值。但是需要注意的是,如果后面的参数没有使用缺省参数,不允许仅对前面的参数使用缺省参数。4.4.4赋值兼容与调用约定赋值兼容是指变量可以进行赋值或进行参数传递。当两个类型要进行赋值操作而又不满足赋值兼容时,将产生编译错误。ObjectPascal提供了五种过程和函数的调用方式,分别为Register,Pascal,Cdecl,Stdcall,SafeCall。缺省的调用方式是Register方式。Register和Pascal调用方式传递参数是从左到右,而Cdecl,Stdcall和Safecall调用方式传递参数则是从右到左。Register调用方式自动清除调用所使用的堆栈和寄存器,负责处理调用错误,同时也是速度最快的调用方式。4.5 变量的作用域变量的作用域变量的作用域是指变量可以被识别的范围。4.5.1公有变量和私有变量一般Delphi的单元具有下面的结构unit单元名 /单元首部interface /单元接口部分implementation /单元实现部分 end. /单元结束在单元的接口部分(Interface)声明的变量属于公有变量,不仅可以被本单元中的所有过程和函数使用,同时还可以被其他单元中的过程和函数使用。在单元的实现部分后声明的变量属于私有变量,不能被其他单元使用。4.5.2全局变量和局部变量局部变量是指在过程或函数的内部声明的变量;而定义在单元的实现部分的变量,对整个单元内部的过程和函数都是有效的,是全局变量。公有变量也是全局变量。当全局变量和局部变量的名称相同时,在过程和函数的内部,使用的是局部变量的值。4.5.3变量的存储方式从空间上来讲,全局变量的作用范围是整个程序,局部变量仅在本程序段内部有效。从变量的存储时间上来看,全局变量是静态存储,局部变量是动态存储。所谓的静态存储是变量在程序运行期间一直占有固定的存储空间,直到整个程序结束变量所占用的空间才释放。而动态存储则是程序在运行期间根据需要动态的分配存储空间,子程序一旦结束,变量所占有的存储空间立即释放。一般内存中供程序使用的区域可以分为三个部分,程序区,静态存储区和动态存储区。在动态存储区中存放的数据有:函数或过程的形式参数,函数和过程内部声明的局部变量以及函数和过程调用时的现场保护和返回地址等。第第5章章 高级数据类型高级数据类型5.1 枚举类型枚举类型5.1.1枚举类型的定义与变量声明1.枚举类型的定义枚举类型使用一组有限的标识符来表示一组连续的整数常数,它的值是有限的。枚举类型的定义格式如下:type=(,);说明:(1)type是系统的保留字,表示定义高级数据类型的开始。(2)表示该类型数据中的元素,圆括号中列出了该类型数据的所有取值,这些取值又称为枚举常量。(3)同一个枚举常量不允许重复出现在同一个枚举类型定义中,也不允许同时出现在不同的枚举类型定义中。2.枚举类型变量的声明其声明的格式与其他类型变量的声明完全相同例如:Var C: Color;该语句声明了一个枚举类型Color的变量C。5.1.2枚举类型的运算1使用函数ObjectPascal为枚举类型定义了5个枚举函数,可以进行特殊的运算。说明:(1)枚举类型定义语句中列出的每一个枚举常量都对应一个唯一的序数(整数),称为枚举序数,在缺省情况下,列出的第一个枚举常量对应枚举系数0,以后依次为1、2、3。枚举函数枚举函数功能功能调用格式调用格式Ord求枚举系数Ord(枚举常量或枚举变量)Pred求前趋值Pred(枚举常量或枚举变量)Succ求后继值Succ(枚举常量或枚举变量)Low求第1个枚举常量Low(枚举类型名)High求最后1个枚举常量High(枚举类型名)(2)在定义枚举类型时,排在某枚举常量前一位的枚举常量称为该枚举常量的前趋值,后一位的称为后继值。第一个枚举常量没有前趋值,最末一个枚举常量没有后继值。(3)由于每个枚举常量都对应一个枚举系数,所以枚举常量的序数可以进行算术运算,结果类型为整型。但枚举常量之间不能直接进行算术运算,需要先转换为枚举序数。2.关系运算由于每个枚举常量对应一个唯一的序数,因此可以在枚举常量之间进行关系运算。如在上述定义中,sunsat的值为假(false)。5.2 子界类型子界类型5.2.1子界类型的定义子界类型的定义格式如下:type=.;说明:(1)表示子界类型的下界,即最小值,表示子界类型的上界,即最大值。子界的上下界必须属于相同的顺序类型,即它们应同时为整型、布尔型、字符型或同一个枚举类型。(3)子界的上下界所属的数据类型即为子界的基类型,若子界的基类型为标准数据类型(整型、布尔型、字符型),则子界的上、下界可以直接使用该类型常量,若子界的基类型为枚举类型,则必须先定义基类型(枚举类型),再定义子界类型。(4)子界的上界必须不小于下界。5.2.2子界类型变量的声明其声明格式与其他类型变量的声明完全相同,如下面的代码声明了一个上述子界类型month的变量ml和workday类型的变量wl:varml:month:wl:workday:5.2.3子界类型的运算子界类型所允许的运算与其基类型所允许的运算相同,如基类型为整型子界类型变量可以进行算术、关系等运算,而基类型为枚举类型的子界类型变量仅能进行关系运算。5.3 集合类型集合类型集合结构是指具有相同性质的对象的全体,构成集合的每个对象称为集合的元素。注意:(1)集合中的元素是互异的、无序的。(2)集合元素个数不能超过256个。(3)元素与集合的关系是“属于”或“不属于”,二者必取其一且仅取其一。5.3.1集合类型的定义其定义格式如下:type = set of;说明:(1)表示集合中各元素的类型,可以是字符型、布尔型、枚举型和子界等顺序类型,不能是整型、实型和其他的构造类型。(3)若为枚举类型或子界类型,则必须先定义该基类型,再定义集合类型。(4)一个集合最多只能有256个元素。另外,只有有序的类型才能跟关键字setof.5.3.2集合变量的声明其声明格式与其他类型变量的声明完全相同。5.3.3变量集合的取值集合变量不同其他变量,它不是一个单独元素,而是一系列元素的一集合。集合变量的取值称为集合值,其一般表现形式如下:,如果集合类型的基类型有n个元素,则该集合类型变量的取值有2n个,包括一个空集合()。5.3.4集合类型的运算集合类型的数据可以进行3大类运算:一类是集合对集合的并、交、差运算,其结果为集合值;一类是集合的关系运算,其结果是逻辑值;一类是元素对集合的“属于”运算,其结果也是逻辑值。运算名称运算名称表示方表示方式式运算结果运算结果是否满足是否满足交换律交换律并运算S1+S2两个集合中所有不重复元素组成的新集合是交运算S1*S2两个集合所共有的元素组成的新集合是差运算S1-S2所有属于S1但不属于S2的元素的集合否相等运算S1=S2如果S1与S2所包含的元素完全相同,则结果为True,否则为False是不等运算S1S2 如果S1与S2所包含的元素完全不同,则结果为True,否则为False是包含运算S1=S2 如果S2中的元素都在S1中,则结果为True,否则为False否被包含运算S1=S2 如果S1中的元素都在S2中,则结果为True,否则为False否属于运算XinS1如果元素X与集合S1的基类型相同,且被包含在S1中,则结果为True,否则为False.否集合运算符具有不同的优先级,如表所示:5.4 数组与记录类型数组与记录类型数组类型(Array)是一些具有相同类型的元素按一定顺序组成的序列。数组中的每一个数据元素都可以通过数组名来存取,它们被顺序安排在内存中的一段连续的区域中。ObjectPascal提供的数组分为静态数组和动态数组。而记录类型可以将不同的数据集中优先级 运算符操作数类型结果值类型高*集合集合中+集合集合低=、=、=集合逻辑最低in左操作数为元素,右操作数为集合逻辑在一起,并作为一个整体进行操作。5.4.1静态数组静态数组在程序初始化时必须分配内存单元,明确其固定的大小和元素的数据类型。1一维静态数组数组通常分为为一维、二维和多维数组,定义一维静态数组类型的格式为:type=arrayof;ObjectPascal允许的下标的类型为整数类型、字符类型、布尔类型、子界类型、枚举类型等,而元素的类型可以是任意的数据类型,并且在同一数组中,所有元素的数据类型必须相同。对于用户定义的数据类型作为下标类型,在使用之前必须声明。要访问数组中元素,可以用数组名加方括号,方括号内是元素的下标值,方括号内的下标值必须符合数组类型中下标类型的定义,其类型必须与下标类型一致,其值在下标取值范围内。而且下标也可以是表达式。使用ObjectPascal提供的标准函数Low和high,可以返回一个数组的最小下标值和最大下标值,而函数Length可以返回数组的长度。2二维静态数组二维数组是指一个一维数组中的元素类型又是一个一维数组,其一般形式为:type =Arrayof Arrayof ;也可以把上述形式写成下面的形式:type=Array,of ;3多维静态数组多维静态数组的一般格式:type =Array,of ;5.4.2动态数组动态数组在定义和声明时仅指定数组的类型,而不指定数组的大小,只是在程序设计中为程序动态地开辟存储空间。1一维动态数组一维动态数组的定义格式如下:type=array of 也可以在变量声明中直接声明动态数组,其格式为:var :array of ;动态数组的声明中没有给出数组的下标类型,因此具有不确定的大小。动态数组的大小通过调用标准过程Setlength来明确。2多维动态数组声明多维动态数组采用递归定义的方式,如下:type=array of array of array of ; var :或者采用如下方式定义多维动态数组变量:var : array of array of array of ;多维动态数组声明后,使用Setlength过程设置动态数组的大小。5.4.3记录类型1记录类型的定义记录类型定义的格式如下:type =Record:;:;.:;end;其中,可以是多个合法的域名标识符,域名又称为字段名,可以是任意数据类型。同一个记录类型中不能有同名的字段,而因为作用域的不同,记录内的字段名与记录外的标识符可以相同。2记录域的访问由于记录类型中各字段的类型不同,所以不能同时访问记录的多个字段,而只能对记录的单个字段进行访问。有两种方法:(1)记录变量名限定为了标识记录字段所属的记录变量,使用记录变量名进行限定,格式如下:.则为记录的单个字段赋值可以使用如下语句:C1.Custid:=1;C2.IfPay:=True;(2)使用With语句如果需要经常访问记录的字段,每次都用记录变量名进行限定非常麻烦,可以使用With语句加以简化。With语句格式如下:With DO 其中,可以是简单语句,也可以是复合语句。在中字段的访问不需要加记录变量名进行限定。3记录的变体部分带有变体部分的记录类型的声明格式为:type =Record:;:;.:;Case : of :;:;:;end;注意:(1)Case前面的声明部分同平常的记录类型声明一样,但如果记录域中含有变体部分,则变体部分应位于记录域的最后。(2)变体部分总识别字段标识符是可选的,省略时连同“:”号一起省略,在同一记录域中必须是唯一的。识别字段类型必须是顺序类型,如果是枚举或子界类型,则必须事先声明。其中的字段列表i同普通的记录类型中域名表的声明相同。其功能应用类似于选择结构中的Case语句。5.5 指针类型指针类型指针是一种特殊的数据类型,指针类型(Pointer)的变量称为指针变量。指针变量具有一般变量的三个基本要素,即变量名、变量类型、变量值,它与一般变量的不同,它是用来存放其他变量内存地址的一种变量。5.5.1指针变量的声明定义指针类型的语法如下:type=其中,可以是基本数据类型,如整型、实型、字节型等,也可以是高级数据类型,如集合、数组、集合等类型。5.5.2指针变量的赋值为指针变量赋值的格式如下::=其中,“”操作符是个一元操作符,用于获取操作数的内存地址,后面的操作数可以是变量、过程和函数等。5.5.3无类型指针变量无类型的指针是指指针变量在声明时没有指明基类型,无类型指针在声明中只使用Pointer,其声明格式如下:var:Pointer;无类型的指针的作用是它可以指向任何类型,对于无类型指针,不能用指针变量符号后加的形式来引用它的动态变量。5.5.4字符指针类型字符指针类型即Pchar数据类型,是一个指向以NULL字符结尾的字符串的指针。主要用于与外部函数如在WindowsAPI中所用的函数兼容。在Delphi7中,可以把一个字符串直接赋值给一个Pchar类型的变量。5.5.5指针变量的动态使用1New过程和Dispose过程如果不使用运算符为指针变量赋值,则指针变量称为动态指针变量,动态变量在访问之前必须首先分配内存单元。ObjectPascal提供了标准过程New,用来为动态变量分配内存单元,并把该单元的地址赋给指针变量,所分配单元的大小由指针所指的类型决定。如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常。调用New过程的格式如下:New();调用过程New(p)之后,可以用“p”表示一个整型的动态变量,对其进行操作。当程序不再需要使用动态变量时,就调用标准过程Dispose删除New所创建的动态变量,并释放所分配的内存单元。调用Dispose过程的格式如下:Dispose();2GetMem过程和FreeMem过程标准过程GetMem用于为动态变量申请一块指定大小的内存区域,并把该区域的起始地址赋给指针变量。如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常。调用GetMem过程的格式如下:GetMem(,);如果程序不再需要使用动态变量时,就调用标准过程FreeMem删除GetMem创建的动态变量,并释放所分配的内存单元。调用FreeMem过程的格式如下:FreeMem();3动态指针的应用举例链表是一组元素的序列,在这个序列中,每个元素总是与它前面的元素相链接(第一个元素除外)。这种链接关系可通过指针来实现。地址13地址212地址3 19地址4 7Nil图5-8 自然数链表链表中的元素通常称为节点,第一个节点称为表头,最后一个节点称为表尾。指向表头的指针称为头指针,在这个头指针中存放着表头的地址。节点一般用记录来描述,描述节点的记录至少含有两个域,一个域用来存放数据,该域的类型根据要存放的数据类型而定,称为值域;另一个域用来存放下一个节点的地址,称为指针域。表尾不指向任何节点,其指针的值为Nil。节点可以通过记录类型来描述,并且记录类型里包含一个指针域,链表节点的声明如下:typeNode=recorddata:string;next:Nodeend;varHead:Node;/定义头节点变量Head;或者采用如下方式:typeLink=NodeNode=recorddata:string;next:Nodeend;varHead:Link;第第6章章 程序异常处理与调试技术程序异常处理与调试技术在Delphi中有两种程序错误,一种是编译错误,在程序编辑阶段就可以由编译器发现并给出提示。另外一种是运行错误,这类错误不能在编译阶段查出,只能在程序执行时发现,称为运行错误。Delphi提供了一种机制来处理运行错误,保护程序的正常执行,这种机制就是异常处理。异常处理的方法是把正常的执行程序同错误的处理程序分离开来,这样可以保证在没有错误时,程序正常执行,当发生错误时,执行错误处理部分的程序,然后程序跳出保护模块,继续执行后续的程序。6.1 异常处理的目的异常处理的目的为了避免程序在执行中出现可能超乎预料结果而导致错误的状况,然后对这些异常的状况做妥善的处理,不让异常的状况造成错误的结果。致使程序异常停止,这就是异常处理存在的目的。可能会认为所开发的程序是可以执行的,因此不需要异常处理,然而当程序编译后没有错误发生时,并不表示程序就完美无缺,事实上某些异常状况是在执行中才发生的。除此之外,有时程序还会受软、硬件环境的影响而发生程序异常的情况。异常处理,可以说是预防程序执行时发生异常而中断的一道防线,通过异常处理可以设法让程序避开异常的发生,不让它异常中断;或者在中断程序前,对数据做适当的处置,而不致丢失重要的数据。6.2 Object Pascal异常的种类异常的种类异常类的种类,主要可以分为两大类,一种是Delphi内建的异常类,另一种则是程序员自定义的异常类。异常基类及其属性和主要方法在Delphi中,所有异常的基类是Exception类。所有其他异常类都是由该类派生而来。1exception属性该类有两个基本属性:HelpContext和Message。(1)Exception.HelpContext属性该属性的定义如下:Type ThelpContext= -MaxLongint.MaxLongint;Property HelpContext:ThelpContext;HelpContext是ThelpContext类的一个实例,它提供了与异常对象联系在一起的上下文相关帮助信息的序列号。该序列号决定当发生异常时用户按F1键显示的一个异常错误的帮助信息。(2)Exception.Message属性该属性的定义如下:property Message: string该属性存储异常发生时的错误信息。可以通过该属性在提示错误对话框中显示错误信息字符串。2exception方法(1)Exception.Create方法该方法的定义形式为:Constructor Create(Const Msg: String);该方法用来产生一个带有一条简单提示信息的对话框,对话框中的提示内容由Msg提供(2)Exception.CreateFmt方法该方法的定义格式如下:Constructor CreateFmt(Const Msg:String;Const Args:Array of Const) ;该方法用来产生一个带有格式化字符串提示信息的对话框,格式化的字符串由Msg和Args数组共同提供,其中数组Args负责提供用于格式化的数值。(3)Exception.CreatHelp方法该方法的定义格式如下:Constructor CreateHelp(Const Msg:String; AhelpContsxt:Integer) ;该方法产生一个带有一条简单提示信息和上下文帮助序列号的提示对话框。其中Msg参数包含了显示在异常对话框中的运行错误信息。AhelpContext参数包含一个限定异常错误信息上下文帮助序列号。6.2.1Delphi内建的异常类Delphi内建的异常类有很多,但基本上各种异常类都是继承自Exception类,而Exception类则继承自TObject类,它们全都定义于【Sysutils】这个资源文件里,然而异常类并不同于一般的类,因此Delphi内建立异常类其标识符的第一个字母都是“E”,如此我们很容易就能辨认出此种类。6.2.2自定义异常类虽然Delphi内建的异常类有很多,但是这些类不见得完全符合我们开发程序的需求,这时我们可以自定义一个异常类,然而异常类和一般的类的自定义有些细微的差别,它必须继承内建类。事实上,自定义的异常类必须继承内建的Exception类,或者继承Exception的某个子类才行。除此之外,自定义异常类的语法和自定义一般类的语法并没有不同。6.3触发异常的方法触发异常的方法触发异常的方法,主要可分为两种,一种是由程序系统自动触发,一种则是利用raise指令触发6.3.1由程序系统自动触发只要属于Delphi内建类的异常产生时,程序系统就会在当下自动触发它们,并捕捉其信息,然后将异常的信息以对话框显示出来,这些是一般公认的异常状况,即使我们不对这些异常做处理,程序系统也会帮我们做处理,然后让程序再继续执行下去,这样程序就不会在当时异常中断,而出现意料之外的问题。不过程序系统所作的只是一般的处理,通常仅是避开执行会发生异常的程序代码,而不会排除掉异常发生的原因。故若保持原来的状态再做同样的执行操作,仍旧会触及同样的异常,却无法执行下一步的程序。因此为了让程序执行更顺畅,并且让用户更容易使用我们所开发的应用程序。即使是程序系统自动触发的异常,我们也应该主动去处理,设法去除导致异常的原因。或者给予用户更明确,更人性化的提示,尽量不要让用户感到任何操怍上的困难,并且避免异常重复发生而浪费不必要的时间。6.3.2使用raise指令触发当然我们可以根据需要,自行触发某个异常,也就是让异常对象产生之后再对异常做处理而自行触发异常的方式就是使用raise指令其语法如下:Raise 异常对象实体异常对象实体不要将raise指令当成一般语句使用,它必须配合异常处理语法来使用。6.4处理异常情况处理异常情况专门用来处理异常情况的语句主要有两种,一种是“try_except_end”结构,另一种则是“try_finally_end”结构。由于Delphi在程序设计时,提供了调试器(Debugger),因此当程序执行时若发生异常状况,调试器将发挥功能,让程序在异常发生点,并且提示调试的方法,方便找出问题所在。然而这样程序就无法如实展现异常处理的情况,而且这个应用程序若不在Delphi环境下执行,也不会有调试器存在。因此在设计异常处理程序时,点选【Tools】|【DebuggerOptions】|【General】选项,然后取消【Integrateddebugging】选项,这样才能看到异常处理的效果。6.4.1TryFinallyEnd结构只需要触发异常,程序系统将自动捕捉被触发的异常,然后以信息对话框显示出异常的信息,让程序避开发生异常的程序代码,然后向下执行程序。无论在“TryFinaly”区内是否有异常被触发,都会接着执行“FindlyEnd”区的语句。然而若是在“TryFinally”区内有异常产生并被触发时,就会由异常发生点跳转此区域,转而执行“FinallyEnd”区的所有语句。try try 没有异常没有异常 发生异常发生异常finallyfinallyendend运行界面图图6-3 TryFinallyEnd执行方式TryFianallyend的一般结构为:try语句语句 /预期可能产生异常的语句 finally语句语句 /无论是否发生异常都要执行的语句 end;此结构中可以编写语句的区域有两个,而且其内语句使用目的并不相同。1TryFinally区中的语句本区可包含多个语句,但这些是可能造成异常情况的语句。而此处产生的异常,包括由程序系统自动触发及程序员使用Raise指令触发的异常。而无论使用Raise指令,还是由程序系统自动触发的异常,程序系统都会在其后“FinallyEnd”区执行完了时,自动捕捉被触发的异常,并且将异常信息显示出来。2FinallyEnd区中的语句本区也可以有多个语句,但是不要在本区使用Raise指令,因为在上一区中由程序系统或raise指令触发的异常,其异常实体将存在本区,并在End关键字前显示异常信息。倘若在本区使用Raise指令,则不管在“TryFinally”是否有异常触发,都会执行Raise指令,并且显示异常信息,请勿在这个区区域使用Raise指令。6.4.2tryexceptend结构使用tryexceptend语法来处理异常时,可自行捕捉异常,然后根据异常的类型不同,对异常做不同的处理操怍。当“tryexcept”区内没有异常被触发时。此区程序执行完之后,会跳过“execptend”区内的程序代码而离开“tryexceptend”区域,直接执行其后的程序代码。反之,若“tryexcept”区内有异常被触发,则在触发异常的情况下,就立即由异常产生点跳出“tryexcept”区,转而执行“exceptend”区的程序。TryExceptend的一般结构为:try语句语句 /预期可能产生异常的语句except语句语句 /捕捉异常的语句 else 语句语句 /可有可无的区域,一般语句(包括raise指令)end;1tryexcept”区中的语句本区可以有多个语句,但这些是有可能造成异常情况的语句,而这里可能产生的异常,try try 没有异常没有异常 发生异常发生异常exceptexceptendend运行界面图图6-7 Try_except_End执行方式包括由程序系统自动触发以及程序员使用raise指令去触发的异常,故在本区可根据状况条件来使用Raise指令。然而在本区使用raise指令,或者由程序系统自动触发某些异常时,程序系统并不一定会自动处理这些异常,这时程序就有可能会异常中断,因此需要“ExceptEnd”区中捕捉异常,并且对异常作适当处理;也可仿照“TryFinallyEnd”语法,在“ExceptEnd”区对“TryExcept”区内被触发的异常作再次触发(Reraise)的操作,即再次使用Raise指令,由程序系统自动捕捉异常,以信息对话框显示出异常信息,然后让程序避开异常,而不致于中断程序。2“ExceptEnd”区中的语句在“ExceptEnd”区中,可以有多个语句,但此处主要是放置用来捕捉异常的语句,其目的是让程序仍自行捕捉异常,根据异常的类型决定要做的处理操作,而此种语句也有它特定的语法:On 异常对象标识符:类型异常对象标识符:类型 do /异常对象标识符可有可无语句;语句;/(on identifier:type do statement)上述语法是表示当指定类型的异常被触发时,就执行保留字“do”后面这个语句。反之若没有这种类型的异常被触发,则不会执行“do”后面的语句。在捕捉异常的语句之后,还可以有一个“Else”区,在这个区域内可以有一般的语句(包括raise指令)。若本区域内没有“Else”区域时,只要其内有捕捉异常的语句存在,就不允许有一般语句(包括raise指令);倘若本区内若有“Else”区,则除了“Else”区域之外,并不允许有一般语句存在于“ExceptElse”区域,否则将导致编译错误。6.5 程序调试程序调试Delphi提供了一个功能强大的内置调试器(IntegratedDebugger),该调试器可以方便地查找程序中出现的运行时间错误和逻辑错误。所谓运行时间错误是指程序能正常编译但在运行时出错。逻辑错误是指程序设计和实现上的错误。6.5.1调试的准备1激活内置调试器方法是:在Delphi集成开发环境中,选中【Tools】|【DebuggerOptions】|【General】页的【IntegratedDebugging】复选框。默认情况下该框被选中。2设置编译和调试选项默认情况下,Delphi对有些错误和信息不给出调试信息。可改变Delphi默认设置。单击【Project】|【Options】|【Compiler】页。(1)RuntimeErrors区域Rangechecking:检查数组或是字符串的下标是否越界,默认时不检测。I/Ochecking:检测输入输出错误,默认检测Overflowchecking:整型操作溢出检测,默认不检测。选中该复选框调试器将对整数运算是否溢出做检测,默认下不报告错误。(2)Debugging区域设置调试的信息。默认时几乎全部选中。一般无须改变该区域的选项设置。Debuginformation:表产生调试信息。如果DebugInformation选中会在单元文件(.dcu)中放置调试信息,文件字节变大但不影响速度。Localsymbols:产生局部变量的调试信息。LocalSymbols选中会添加与所在类、过程、函数及对象方法中定义的标识符等有关调试信息。在程序调试时调试器会使用这些信息,但这些信息不会添加到可执行文件中。除非在【Project】|【Options】|【Linker】页面中选中【IncludeTD32DebugInfo】选项,选中了此选项就可以使用TD32来调试。Referenceinfo/Definitionsonly:用来产生供CodeBrowser,CodeExplorerandProjectBrowser使用的标识符引用信息。如果ReferenceInfo和DefinitionsOnly都被选中,则编译器将记录标识符定义位置信息。如果仅选中了ReferenceInfo,表示编译器不仅记录标识符定义的位置,同时将记录标识符被引用的信息。如果不选中DebugInformation和LocalSymbols选项,仅选中该选项将不起作用。Assertions:产生断言的调试代码。UseDebugDCUs:使用连接的Dcu文件作为调试路径。必须在【Tools】|【DebuggerOptions】|【General】页中指定调试文件的路径。一般不选中该项。(3)Messages区域ShowHints:使编译器产生提示信息。例如检测在过程或函数中声明了但一直没有使用的变量信息,或者无效的引用信息等。ShowWarnings:使编译器产生警告信息。3编译程序发现编译错误在调试之前,必须先编译通过。可以选择【Project】|【Complie】可以对工程进行编译,检测编译错误。也可以按【Ctrl+F9】执行同样的操作。默认情况下,如果有错误或是警告和提示信息则显示在Message列表框中。6.5.2控制程序的执行Delphi程序的调试命令都集中在RUN菜单下。可以三种方式进行调试:【StepOver(F8)】单步执行调试、【TraceInto(F7)】跟踪调试或使用、【RunToCursor(F4)】运行到光标所在处。StepOver一次执行一行语句,碰到调用过程时也是一步就执行过去,不会跟踪到过程的内部代码中去逐行执行,TraceInto则是在碰到过程或函数时跟踪到它们的内部,可以对其内部代码进行调试。RunToCursor则从当前运行位置直接运行到光标所在的位置如果光标所在的位置和当前运行位置处在不同的事件代码中,则不能直接运行到光标处,只有当发生了该事件才可以继续执行。6.5.3 使用断点使用断点断点(BreakPoint)就是使程序运行中断的点。在一个应用程序总可以设置多处断点,当程序运行到断点处,会暂停执行,等待进一步的命令。1断点的设置(1)单击选定代码行左边的空白。(2)在光标所在的行处按【F5】。(3)使用【Run】|【AddBreadpoint】|【sourcebreakpoint】打开断点编辑对话框,在LineNumber处输入需要加断点的行号即可。断点必须位于可执行代码行上,另外,断点既可以在设计状态下设置也可以在运行调试状态下设置。一个有效(Enable)的断点默认的情况下该代码行显示为红色,正确的断点小圆点中是一个对号。2断点的删除和设置删除一个断点,只要再次在已经设置为断点的代码行单击其左侧的空白处或按【F5】键就可以删除断点。如果一个应用程序许多位置都设置了断点,则可以使用断点列表框来管理所有的断点。使用【View】|【Debug】|【breakpoints】打开断点列表框,列表框将列出应用程序中设置的所有断点,无效(Disable)的断点前面的标志为灰色。在列表窗口中单击右键,将显示一个断点设置快捷菜单,使用该快捷菜单可以实现对断点的添加、删除、使有效以及无效等操作。(1)利用断点列表窗口可以快速找到断点在源代码中的位置(2)断点功能的失效和恢复在断点列表窗口单击右键,在快捷菜单中取消对Enable的选择或选择【breakpoints】|【DisableAllBreakPoints】项可以使当前选中断点或所有断点失去功能。快捷菜单中的【EnableBreakPoint】和【EnableAllBreakPoint】可以使相应断点恢复功能。同样快捷菜单中的【DeleteBreakPoint】和【DeleteAllBreakPoint】可以删除当前选中断点或所有断点。3修改断点属性在断点列表窗口选择断点后单击右键,在弹出的快捷菜单中选择Properties,则打开断点编辑对话框,用于显示和修改断点属性。也可以使用【Run】|【AddBreadpoint】|【sourcebreakpoint】打开该对话框。利用该对话框可以改变断点的位置,设置断点条件。断点条件包括两种:布尔表示式和通过次数。Condition编辑框用于设置布尔表达式条件。如果表达式值为真(或非零)则程序运行在断点处中止;否则调试器将忽略该断点。PassCount编辑框用于设置通过次数条件,即只有当程序运行在该断点处通过设定次数时程序运行才在该断点处中止。同时设置时,PassCount是指满足条件的通过次数。6.5.4 监视数据的值监视数据的值1监视表达式选择【View】|【DebugWindows】|【Watches】可以打开监视列表窗口WatchList。在该窗口中单击鼠标右键,在弹出的快捷菜单中选择AddWatch打开监视属性对话框,可以添加新的变量或表达式。也可以使用【Run】|【AddWatch】打开监视属性对话框。在Expression右边的编辑框中添加要监测的变量或表达式,同时设置其属性。当该表达式代表一个数据元素时,可以在Repeatcount中指定其重复次数。如果要监测的是一个数组的值,可以使用Repeatcount指定数组元素的下标。2计算/修改表达式选择【Run】|【Evaluate/Modify】可打开计算/修改对话框。当单击Evaluate按钮时,Expression编辑框中表达式的值显示在Result域中。Expression中可以输入或选择任何合法的表达式(包括对象的属性),但不能包括;(1)包含有当前执行点不能引用的局部或静态变量的表达式;(2)函数或过程调用。Expression中的表达式可以带特定的格式字符用于规定其显示格式。其表示语法格式为:变量名,格式字符串。可使用的格式字符及其功能如下:H,X:以十六进制格式显示整型值D:以十进制格式显示整型值C:把ASCII码在0.31的特殊字等显示为ASCII码图形Fn:用n个有效数字显示浮点数nM:以十六进制方式显示一变量的内存转储值,n限定要显示的字节数。P:以段和偏移量格式显示指针。两部分皆为四位十六进制值Modify按钮可以修改特定表达式的值。一般修改表达式的值常用于验证错误解决方案的正确性。在Expression编辑框中输入欲修改的表达式,单击Evaluate按钮观察表达式的当前值。而后在NewValue编辑框中输入或选中一个新值,单击Modify按钮确认并更新数据项。但一般不要修改指针和数组下标单击Watch按钮可以打开监视列表窗口,并添加选定的表达式到列表窗口中。单击Inspect按钮可以为选择的数据元素打开一个信息的窗口,这对于观测数据结构、类和数组的值特别有用。3函数调用选择【View】|【DebugWindows】|【CallStack】可以显示调栈窗口(CallStackWindow)。调栈窗口的顶端列出了应用程序最近的函数调用。利用调栈窗口可以退出当前跟踪的函数,可以利用快捷菜单项显示或编辑位于特定函数调用处的源代码。4观测局部变量当调试的程序位于某一个过程或函数内部时,可以选择View|DebugWindows|localvariables可以打开局部变量显示窗口。该窗口显示在该过程或函数内部使用的所有局部变量及其值。第第7章章 键盘、鼠标和文件编程键盘、鼠标和文件编程7.1键盘的编程键盘的编程7.1.1关于键盘在计算机发展过程中,一开始是就使用键盘作为输入方式,在DOS环境中,大多只有判断键盘是否单击,以及单击的是哪个键,在Windows之中,可以判断键盘单击的事件有三种如下图所示:在这些事件中,OnKeyDown和OnKeyUp都会传入用户单击的Key值(word值),可以利用这些值,来判断用户按了哪些键,而这些值代表了Windows中的VirtualKeyCode。而OnKeyPress所返回的是一个Char值,代表一个ASCII字符。ASCII字符和VirtualKeyCode是不相同的,因为VirtualKeyCode中有代表ASCII的字符,但ASCII字符不包含全部的VirtualKeyCode,因为VirtualKeyCode中内含了很多功能键7.1.2键盘常用事件1OnKeyDown当按下键盘上的任一个键时,就会触发此事件。如字母键、数字键、功能键(F1F12)、Ctrl键、Shift键或Alt键等,都将产生一个OnKeyDown事件。2OnKeyPress当用户单击ASCII字符的键盘时,就是说当按下键盘上的一个字符键,如字母键、数字键等会产生一个OnKeyPress事件,但是单独按下功能键(F1F12)、Ctrl键、Shift键或Alt键等,不会产生OnKeyPress事件。3OnKeyUp当按下键盘上的任一个键后松开时,都会产生一个OnKeyUp事件。对于功能键(F1F12)、Ctrl键、Shift键或Alt键等,也会产生一个OnKeyUp事件。4检测功能键在组件的OnKeyDown、OnKeyUp、OnMouseDown和OnMouseUp等事件的处理过程中,有一个TShiftState类型的变量Shift,TShiftState类型定义如下:TypeTShiftState=setof(ssShift,ssAlt,ssCtrl,ssLeft,ssRight,ssMiddle,ssDouble);根据Shift的值就可以判断当键盘上的键按下时Shift、Alt和Ctrl键的状态,或者按下鼠标左键、中键时的状态或者是否双击了按键。当然,如果有OnMouseDown事件发生了,而又不是按下左键和中键,则按下的一定是右键。7.2鼠标的编程鼠标的编程常用鼠标的事件有以下几种:1常用鼠标事件(1)OnClick:当用户单击鼠标任何一个键时,就会触发此事件。(2)OnMouseDown:当用户单击鼠标时,就会触发此事件。(3)OnMouseMove:当用户单击鼠标在对象上移动时,就会触发此事件,但停止就不触发了。(4)OnMouseUp:当鼠标的某个按键按下,然后松开后会产生一个此事件。在这些事件中,OnMouseDown和OnMouseUp都会触发事件,但是在用户单击时,可能会移动鼠标位置,使得两者被触发的对象不同的,但OnClick和OnMouseDown是会触发在同一个对象上的。当用户在对象A按一下时,A会同时触发OnMouseDownOnClickOnMouseUpOnMouseMove当用户在对象A单击但在对象B放开时,A会触发OnMouseDownOnMouseMoveOnMouseUpB会触发OnMouseMove因此如果要触发OnClick事件,就必须要在一个对象按一下才行,否则只会有OnMouseDown、OnMouseMove、OnMouseUp这些事件。2拖放事件(1)OnDragDrop:在拖曳事件开始时会触发此事件。(2)OnDragOver:当拖曳对象跨过一个组件时会触发此事件。(3)OnEndDrag:当拖曳事件结束后会产生触发此事件。具体过程如下:(1)拖曳操作开始大多数的组件具有DragMode属性,表示开始拖曳操作的方式。DragMode属性的缺省值为dmManual,也就是要在被拖动组件的OnMouseDown事件的处理过程中调用BeginDrag过程才开始拖曳操作。如果将DragMode属性设置为dmAutomation,则鼠标左键在被拖动组件上按下后就自动开始拖曳操作。(2)接受拖曳操作当拖动一个组件经过第二个组件的时候,第二个组件会产生一个OnDragOver事件。在该事件的处理过程中有一个布尔类型的参数,该参数的设置直接影响是否产生OnDragDrop事件。一般情况下,在OnDragOver事件的处理过程中,根据参数Source判断拖曳操作的源。如果是可以接受的源,则将Accept参数设置为True;否则,将其设置为False。(3)处理拖曳操作在第二个组件OnDragDrop事件处理过程中,根据拖曳操作的源做一些相应的处理。(4)拖曳操作结束拖曳操作完成后释放鼠标左键,会在第一个组件中产生一个OnEndDrag事件,可以根据参数Target的数值进行相应的处理。如果参数Target的值为nil,则表示拖曳操作没有被接受;如果Target的值不为nil,则Target的值就是接受拖曳操作的组件。3滚轮事件(1)OnMouseWheel:当用户单击滚轮按钮时,就会触发此事件。(2)OnMouseWheelDown:当用户用滚轮按钮向下转动时,就会触发。(3)OnMouseWheelUp:当用户滚轮按钮向上转动时,就会触发。7.3文件的编程文件的编程Delphi的文件分为文本文件、有类型文件和无类型文件。7.3.1适合于各种文件的基本操作1与外部文件联系的建立与中断在Delphi中要对外部文件进行读写操作前后,需将该外部文件名分配给一个文件类型的变量已经中断文件变量与该外部磁盘文件的联系。(1)文件变量与外部文件建立联系通过调用AssignFile过程可以初始化一文件变量即建立文件变量(F)与外部文件之间的联系。AssignFile过程的声明如下:procedure AssignFile(var F;FileName:string);其中F是一个文件变量,它可以代表各种类型的文件。FileName是一个字符串表达式,代表某个特定的文件名。在调用该过程文件变量(F)将一直和外部文件相联系,直到关闭该文件变量。在由FielName所指定的外部文件中,同样进行对该文件变量的各种操作。(2)文件变量与外部文件中断联系通过调用CloseFile过程可以将中断文件变量(F)与外部磁盘文件之间的联系。CloseFile过程的声明如下:procedure ClsoeFile(var F);其中F是一个文件类型的变量,可以代表各种文件的类型,该文件可以由读方式打开、写方式打开或者由添加方式打开。在对同文件变量相联系的外部磁盘文件修改后,调用CloseFile过程将释放文件变量,并关闭该外部文件。2文件的打开与关闭(1)以读方式打开文件(Reset)。通过调用Reset函数可打开一个已经存在的文件。如果该文件是一个文本文件,那么文件变量(F)的属性为只读。如果指定的文件不存在,则会产生错误,如果指定的文件已经打开,则先关闭再重新打开。当前文件的位置设置在文件的开始。调用Reset后,如果文件为空Eof(F)为True,否则为False。Reset过程的声明如下:procedure Reset(var F:File;RecSize:Word);其中F是一个任意文件类型的变量,RecSize(记录长度)是一个可以默认的表达式,并且仅当F是一个无类型文件时才用,该参数指定数据传送时的文件大小。(2)以写方式打开文件(Rewrite)通过调用Rewrite函数可创建并打开一个新文件。如果F是一个文本文件,那么文件变量(F)的属性为只读。如果文件已经打开,则先关闭然后在重新创建,当前文件的位置设置在文件开始。调用Rewrite后,则Eof(F)必为True。Reset过程的声明如下:procedure Rewrite(var F:File;Recsize:Word);其中F是一个任意文件类型的变量,RecSize(记录长度)是一个可以默认的表达式,并且仅当F是一个无类型文件时才用,该参数指定数据传送时的文件大小。(3)用Erase过程删除文件。通过调用Erase过程可删除一外部文件。Erase过程的声明如下:procedure Erase(var F);其中F是一个任意文件类型的变量,调用Erase过程将删除同F相连的文本文件。3文件的基本操作函函 数数 与与 过过 程程 名名实实 现现 功功 能能functonIOResult:Integer;返回最近一次I/O操作的状态值procedureRename(varF;Newname:string);procedureRename(varF;Newname:PChar);用Newname重新命名文件procedureChDir(S:string);将当前目录改为字符串S制定的目录functionEof(varF):Boolean;Textfiles;functionEof(varF:text):Boolean;检测文件是否结束,如果结束和文件为空则为Ture,否则为FalseprocedureGetDir(D:Byte;varS:string);检测由D指定驱动器的当前目录。D的取值可为(0,1,2,3),其分别代表(默认,A,B,C)procedureRmDir(S:string);删除由字符串S指定的目录procedureMkDir(S:string);新建一个由字符串S指定的目录,改目录原来并不存在7.3.2适合于文本文件的基本操作1以添加方式打开文件(Append)通过调用函数Append可打开一个已经存在的文件以便于在文件末尾添加文本。如果在文件最后的128个字节块中,存在字符+(ASCII26),那么文件将在该字节处插入,并且覆盖该字符。即,文本可被插入到以字符+终止的文件后。Append过程的声明如下:Procedure Append(var F:Text);其中F是一个任意文件类型的变量,并且必须同用AssignFile函数打开的外部文件相联系。如果指定名称的外部文件不存在,就会产生错误。如果指定的文件已经打开,则先关闭再重新打开。当前文件的位置设置在文件末尾。如果分配给F的是一个空名字,则在调用Append函数后,文件变量(F)将同标准输出文件建立联系。2文本文件的读取与写入(1)用Read过程读取数据。通过调用Read过程可以从文本文件中读取字符串、字符或数字。其声明如下:procedure Read(var F:Text;V1,V2,Vn);其中F是文本文件变量,V1,V2,Vn用于存储读取的数据,其必须为相同的类型,可以定义为字符串类型变量、字符型变量或整型变量、实型变量。当V1,V2,Vn定义为字符串型或字符型变量时,则Read过程将按照定义的长度读取字符。当V1,V2,Vn定义为整型变量或实型变量时,则Read过程将以空格作为分隔符,若在数字中出现逗号、分号或其他字符将产生异常。(2)用Readln过程读取数据。通过调用Readln过程可以从文本文件中读取字符串、字符或数字,直到一行的结束。其声明如下:procedure Readln(var F:Text;V1,V2,Vn);其中F是文本文件变量,V1,V2,Vn用于存储读取的数据,可以定义为字符串型变量、字符型变量或整型变量、实型变量。(3)用Write过程写入数据。通过调用Write过程可以向文件中写入数据。其声明如下:procedure Write(var F:Text;P1,P2,Pn);其中F是文本文件变量,P1,P2,Pn用于存放写入的数据,其可以是字符串类型、字符类型、整型或浮点型。(4)用Writeln过程写入数据。通过调用Writeln过程可以向文件中写入一行数据,并在结尾处输入回车换行符。procedure Writeln(var F:Text;P1,P2,Pn);3文件的基本操作函函 数数 与与 过过 程程 名名实实 现现 功功 能能ProcedureAssignPrn(varF:Text);建立文本文件变量同打印机之间的联系FunctionEoln(varF:Text):Boolean; 检测文件指针是否指向行尾ProcedureFlush(varF:Text);输入方式(Rewrite或Append)打开的文件缓冲区,以确保所有写入文件的字符都被写入外部文件FunctionSeekEof(varF:Text):Boolean;返回文件尾状态FunctionSeekEonln(varF:Text):Boolean;返回文件行尾状态ProcedureSetTextBuf(varF:Text;varBuf;Size:Integer);设置文件缓冲区7.3.3有类型文件有类型文件是一种具有一定数据类型的文件,它是由指定数据组成,读写过程所操作对象的单位是一个指定类型的数据。由类型文件的变量可声明如下:Type fileTypeName=file of type 其中fileof为保留字,type可以是各种数据类型如整型、实型及记录型。FileTypeName为类型文件名。1有类型文件的读取和写入方法对于有类型文件允许同时为读和写打开。通过调用Read过程可以从文件中读取数据。其中F,及V1,V2,Vn的定义同文本文件。其声明如下:procedure Read(F,V1,V2,Vn);通过调用Write过程可以向文件中写入数据。其声明如下:procedure Write(F,V1,Vn);2文件的基本操作函函 数数 与与 过过 程程 名名实实 现现 功功 能能FunctionFilePos(varF):Longint;返回文件的当前文件指针位置FunctionFileSize(varF):Integer;返回文件的大小ProcedureSeek(varF;N:Longint);将文件指针移至文件位置ProcedureTruncate(varF);截去当前位置后的所有数据7.3.4无类型文件无类型文件无固定的数据结构,可由使用者决定每个数据记录的长度,声明如下。Var DataFiel:file;在对无类型文件用Reset和Rewrite过程打开时,可带有第二个参数,用来说明数据记录的长度,如果默认则为128B。无类型文件的读取和写入方法通过调用BlockRead和BlockWrite函数可读入或写入一个或多个记录的数据。其声明如下:procdure BlockRead(var F:File;var Buf;Count:Integer;var AmtTransferred:Integer);procdure BlockWrite(var F:File;var Buf;Count:Integer;var AmtTransferred:Integer);其中F是无类型文件变量,Buf用于存储读取或写入的数据,Count则确定了每次应读写的记录的个数,AmtTransferred将返回每次实际读写的记录的个数。在变量中有关系式Buf的大小Count*RecSize(RecSize为记录的长度)。在一般情况下AmtTransferred同Count应相等,但在文件末尾或在磁盘已满的情况下AmtTransferred将小于Count。第第8章多媒体编程章多媒体编程8.1图形图像基础知识在Delphi中,图形图像的产生有种方式:(1)在程序执行时由程序绘制;(2)设计期间使用Shape组件给出;(3)执行期间由用户自己制作;(4)直接读取已存在的图形图像文件。一般来说,最常用的绘图方式是各种几何图形的绘制,如直线、圆和椭圆、矩形等。8.1.1图形图像对象组件与图像种类1图形图像对象组件(1)画布对象(TCanvas):TCanvas是许多组件都具备的一个属性。同时它本身也是一个对象,包含自己的属性,其中最重要的有个:画笔、画刷、字体组件,以及图形像素数组。TCanvas对象提供了作图操作的平面及各种工具,使用这些工具在这个平面上绘制各种线条、曲线以及其他形状。(2)图形对象(TGraphics):TGraphics对象是图像文件在内存中的抽象代表,用于存储图像文件,以便将其从磁盘装入内存,或从内存存放到磁盘。TGraphics有个派生类:TBitmap、TIcon和TMetafile(分别为位图、图标和图元类)。如果知道具体的图像类型,则应将其存储在相应类的对象中,而不是基类TGraphics的对象中。(3)图片对象(TPicture):TPicture对象是图形对象(TGraphics及其派生类的实例)的容器。也就是说,它可以装载TBitmap、TIcon和TMetafile及其他TGraphics类的图。(4)图像组件(Image):Image就是具有TCanvas和TGraphics属性的组件,它在应用程序的窗体上提供一个矩形区域,用于显示和输出图形(组件)。(5)图形组件(Shape):Shape组件在窗体中提供一个可用来绘制几何图形的矩形区域,利用该组件可将绘图操作限定在一个区域内,而不使用窗口的整个客户区进行操作。(6)画框组件(PaintBox):PaintBox组件在窗体中提供一个用来绘制几何图形的矩形区域,可使用绘图语句在这个区域内绘制各种图形。2图形图像文件的种类图形文件种类繁多,常见的有位图、图标、图元,以及各种压缩格式(Jpeg、Gif等)的图形文件。(1)位图(TBitmap):Win32位图是以位形式存储的二进制信息,位图保存了像素的颜色信息。位图是各种绘图工具都支持的通用的图形文件格式。Delphi环境的各种图形对象或组件也都支持位图的存储和显示。(2)图标(TIcon):图标作为Windows资源常以Ico为扩展名保存。它们可以存在于资源文件(res)中。在Windows中,有两种典型大小的图标,一是3232像素的大图标,二是1616的小图标。小图标显示在应用程序主窗口的左上角或列表视图控件中。Delphi环境将这个控件封装为TListView组件,位于组件面板的Win32页。图标由两个位图组成。一个是实际要显示的图像,另一个是图标显示时的蒙版。(3)图元(TMetafile):图元是基于矢量的图像。图元文件是保存了一系列GDI(graphdisplayinterface,图形显示界面)例程的文件,允许将对GDI函数的调用保存到外存。同时,可与其他程序共享作图例程。图元文件可以平滑地改变大小(位图在放大后会失真)。图元文件有两种格式:标准图元文件(.wmf)和增强图元文件(.emf)。DelphiTMetaFile支持这两种图元文件。(4)JPeg图:Jpeg文件扩展名JPG。Jpeg是一种静态图形压缩算法,图像质量可以调节,压缩比率较高。这种文件的读写以及和位图的转换都要经过压缩或者解压。在Delphi7中,如果要操作Jpeg文件,需要在单元中包含Jpeg单元名。8.1.2图像组件(Image)Image组件是一种图像的容器,用于显示各种以文件形式存储磁盘上的位图、图标、图元文件或用户自定义的图形文件。设计阶段指定图片的方法是:单击对象编辑器的Picture属性行的右格中的按钮,打开图片对话框,然后选择一幅图片。在应用程序运行期间,可以调用相关的函数或过程动态地从文件中载入图形图像。Image组件常用属性属性类型作用PictureTpicture 指定图片,可为位图(BMP)、图标(ICO)或图元(EMF、WMF)文件,若为Jpeg图片,当前单元Uses语句中应包含Jpeg单元。CanvasTcanvas 提供图像组件进行绘图操作的平面。AutosizeBoolean 其值为True时,Image自动调节大小以适应图像。默认为True。StrethBoolean 其值为True时,图像自动调节大小以适应Image,但.ICO文件不能。默认为False。Transparent Boolean 默认值为False。确定是否允许图像组件下面的物体显示出来。CenterBoolean 其值为True时,图像居中,否则,从左上角开始显示。默认值为False。8.1.3图形组件(Shape)Shape组件用于在窗体上绘制一些常见的几何图形,如矩形、圆和圆角矩形等。作图时常用的属性有Shape、Brush和Pen等。1Shape属性Shape组件的Shape属性用于指定要绘制的几何图形种类,属于TShapeType类型。该属性可能的取值有:stCircle(图)、stEllipse(椭圆)、stRectangle(矩形)、stRoundRect(圆角矩形)、stRoundSquare(圆角正方形)和stSquare(正方形)。在设计期间,可以通过鼠标拖放改变图形的大小,在运行期间,可以通过Height和Width属性改变图形大小。2Brush属性Brush(画刷)属性指定图形填充的模式和颜色。在对象编辑器中,Brush属性栏中有“”符号,展开后可看到子属性Color和Style。(1)Color子属性:包含一系列预定义的颜色,用作几何图形的填充色。(2)Style子属性:确定几何图形的填充样式,可取8种不同的值。3Pen属性Pen(画笔)属性指定线型、线宽和线的颜色。它也像Brush属性一样包含子属性,它的子属性是Color、Mode、Style和Width。其中最常用的是Style和Width。(1)Style子属性:确定线型。子属性的取值有psSolid、psDash、psDot、psDashDot、psDashDotDot、psClear和psInsideFrame,分别表示实线、破折号、圆点等。(2)Width子属性:表示线宽,默认值为1。8.2画布对象画布对象画布对象Tcanvas本身也是一个对象(组件),包含绘图中使用的各种方法和属性,最重要的有画笔、画刷、字体组件以及图形像素数组4个组件,但一般不能单独使用。8.2.1像素操作像素是构成图形最基本的单位。画布上的每个点都有一个对应的像素,用来代表图形上某点的颜色。一般情况下,并不需要直接存取像素,而是调用画笔和画刷这样的处理像素的工具。直接读取像素也很简单:画布上的一幅画对应一个存储像素的矩阵(二维数组),矩阵中的一个元素代表一个点的颜色。可以读取一个像素或者设置它的颜色。下面语句的功能是:读取一个像素,并将它设置为红色。Canvas.PexelsX,Y:=clRed;8.2.2画笔画布(Canvas)中的画笔(Pen)属性控制线条的宽度、形状和颜色。画笔本身又包含4个可以设置的属性Color、Width、Style和Mode,以及多个画直线和其他图形的方法。1画笔的属性(1)颜色(Color)属性:Color属性设置画笔的颜色,默认为黑色。它的取值有ClBlack(黑)、ClMaroon(褐红)、ClPuple(紫)、ClSilver(银)、ClBackGround(Windows背景色)、ClWindow(窗体色)、ClBtnHignlight(按钮反白色)、cl3Ddkshadow(三维对象阴影色)等。将画笔设置为红色的代码是:Canvas.Pen.Color:= clRed;(2)宽度(Width)属性:Width属性设置画笔的粗细程度(像素个数)。例如,设为两点(默认为1)的代码是:Canvas.Pen.Width:=2;(3)样式(Style)属性:Style属性设置画笔所画线条的类型,其值有psSolid(实线,默认)、psDash(虚线)、psDot(点线)、psDashDot(点划线,宽非1无效)、psDashDotDot(双点划线,宽非1时无效)、psClear(无线)、psInsideFrame(实线,宽大于1抖动)。(4)显示模式(Mode)属性:Mode属性确定画笔与屏幕上原有点的混合方式。可选的值有pmCopyCopy显示模式。默认值pmNotXor使用背景显示模式。2画线的方法(1)Moveto方法,作用是将画笔移到指定位置,使用方法为:moveto(x,y,integer)。(2)lineto方法,作用是画一条到指定位置的直线段,线段起始位置由画布对象的Penpos属性值即画笔的当前位置确定。使用方法为:lineto(x,y:Integer)。(3)画折线的方法,使用方法为Polyline(points:arrayofTPoint)其中Points为类Tpoints的一个数组Points定义为:Tpoint=record X:longint; Y:longint End;Points:array of Tpoint;3画矩形的方法Rectangle方法用于画矩形。使用方法为:Rectangle(x1,y1,x2,y2:integer);其中(x1,y1)为矩形左上角的坐标,(x2,y2)为右下角的坐标。4画圆或椭圆的方法llipse方法用于画圆或椭圆。使用方法为:Ellipse(x1,y1,x2,y2:integer);其中,(x1,y1)为圆或椭圆外切矩形左上角的坐标,(x2,y2)为右下角的坐标。5画弧形曲线的方法Arc方法用于画圆弧形曲线。使用方法为Arc(x1,y1,x2,y2,x3,y3,x4,y4:integer)。其中(x1,y1),(x2,y2)为圆或椭圆外切矩形左上角的坐标及右下角的坐标。使用Arc方法确定的弧线曲线就是该椭圆曲线的一部分,并且(x3,y3),(x4,y4)分别确定了弧形曲线的起始点。这样就可由起始点,在椭圆曲线上沿逆时针方向得到该弧形曲线。6.圆角矩形Roundrect(x1,y1,x2,y2,x3,y3:integer);其中(x1,y1),(x2,y2)确定了直角矩形左上角、右下角坐标。x3,y3为圆角长短半径。7写字符串TextOut(x,y:Integer,Const text:String);TextRect(Rect:Trect;x,y;Integer;Const text:String);TextOut和TextRect方法用于在画布指定位置或矩形区域内绘制字符串。8.2.3画刷与作图区域画布的画刷(Brush)属性决定图形内部区域的填充方式。画刷属性本身又包含4个属性:Color颜色属性、Style风格属性、Bitmap位图属性和Handle属性。其中Handle属性提供对WindowsGDI对象句柄的访问。1画刷的属性(1)颜色(Color)属性:Color属性设置画刷填充区域的颜色,默认为白色。例如,将画刷设为蓝色的代码是:Canvas.Brush.Color:=clBlue;(2)样式(Style)属性:Style属性用于设置画刷的填充区域样式,其值有:psSolid(实线)、bsClear(空白,默认值)、bsFDiagonal(主对角线)、BsHorizontal(水平线)、bsVertical(垂直线)、BsBDiagonal(副对角线)、bsCross(十字线)、bsDiagCross(对角交叉线)。(3)位图(Bitmap)属性:Bitmap属性是一个存放图形数据的对象。它允许在窗体上指定一块区域放入图形或图像对象,并允许用户在程序运行过程中调整图形图像的大小。位图对象一般可在程序运行阶段动态地创建或删除。位图常用的命令有:bitmap.creat /通过执行creat命令创建一个位图图像bitmap.loadfromfile(文件路径文件路径) /通过文件路径调入位图,装载在位图对象中bitmap.free /释放位图对象bitmap.draw /在指定位置按原图大小显示2作图区域Rect属性是类TRect属性的对象,同时也是一个函数。Rect对象的作用是定义一个矩形区域对象,而作为函数使用时则用于定义此区域的具体范围或者就是绘制一个矩形。Rect对象用两个坐标点(Tpoint类型)指定区域范围,或者用4个整型变量定义区域范围。这些属性是:TopLeft(左上角)、BottomRight(右下角)、TopLeft.X(左上角X)、TopLeft.Y(左上角Y)、BottomRight.X(右下角X)、BottomRight.Y(右下角Y)。Canvas有三个绘制图像的方法:(1)Draw(x,y:ineger,Graphi:Tgraphic):其中x,y为绘图区域右上角的坐标。Graphi参数指明需要绘制的图像、图标或图元文件。(2)procedure FillRect(cost Rect:Trect):其中Rect为绘区域,在调用该过程之前,先由Brushi.bitmap指明需要绘制的图像、图标或图元文件。(3)Strecthdraw(Rect:Trect,Graphi:Tgraphic):其中Rect为绘图区域。8.2.4PanitBox画框组件PanitBox在窗体中提供一个可以用来绘制几何图形的矩形区域,可以使用绘图语句,在这个区域内绘制各种图形。PanitBox组件在system页中,双击则可在窗体中添加。8.3音频和视频播放音频和视频播放8.3.1音频播放Delphi环境的MediaPlayer组件封装了WindowsMCI(multimediacontrolinterface,多媒体控制接口)的大量函数。Windows媒体播放器支持的格式,如WAV、MID、DAT、AVI、MPG、MPA、MP3、ASF、WMV、WMA和CD等,都可以用它来播放。如果播放通用的WAV(Windows标准声音格式)文体,则不必调用MediaPlayer组件,改为调用MMSystem单元中的PlaySound函数即可。PlaySound函数用于播放WAV声音文件,或者播放一种默认的系统声音文件,在播放声音的同时,可以继续执行应用程序的其他功能,也可以暂停应用程序,直到声音播放完毕。调用PlaySound函数的语法格式为:PlaySound(声源,资源,播放方式);其中3个参数的意义如下:(1)“声源”参数:指定要播放的声音文件,可以是带路径的文件名、资源文件名或Win.Ini文件中Sound部分的条目,也可以是指向内存某处声音的指针。(2)“资源”参数:包含声音资源的可执行文件句柄。如果“播放方式”参数未设为SDN-RESOURCE,则该参数必须设为零。(3)“播放方式”参数:指定如何播放。可以是下列值的任意组合:SDN_YNC、SDN_ASYNC、SDN_NODEFAULT、SDN_MEMORY、SDN_LOOP、SDN_NOSTOP、SDN_NOWAIT、SDN_ALIAS、SDN_ALIAS_ID、SDN_FILENAME、SDN_RESOURCE、SDN_PURGE、SDN_APPLICATION、SDN_ALIAS_START。8.3.2卡通控件卡通控件是Windows(Windows95以上版本)提供的具有媒体播放能力的窗口控件,可连续播放无声的AVI剪辑文件。AVI文件格式是微软公司标准的音频和视频文件存储格式。在AVI文件中,音频和视频交织存储,每帧都有音频和视频,且实时、直接地送往音频和视频硬件。缺陷是难以编辑和适应扩充了的多媒体“展播”。Delphi环境的卡通控件Animate封装了Windows的卡通控件,它的属性和方法如下:1Animate组件的主要属性(1)FileName属性:制定要播放的AVI剪辑文件名称。(2)Open属性:确定AVI剪辑是否装入内存。(3)StartFrame属性:设置AVI剪辑播放的起始帧。其值为1时,从装入第1帧播放,为2时从第2帧播放。(4)StopFrame属性:设置AVI剪辑播放中止帧(5)Active属性:判定是否正在播放AVI剪辑文件。(6)AutoSize属性:确定卡通组件窗口是否根据AVI窗口大小而变化。(7)Center属性:值为True时,播放窗口位于计算机屏幕中央。(8)Repetitions属性:确定AVI剪辑和重复播放次数。2卡通控件的方法有:(1)Play方法:播放AVI剪辑文件,方法原型为ProcedurePlay(FromFram,ToFram:Word;Count:Integer);其中,FromFrame指定起始帧,ToFrame指定终止帧,Count指定播放次数。(2)Reset方法:使组件复位为默认值。方法原型为:ProcedureRset;(3)Seek方法:播放所指定的帧。方法原型为:ProcedureSeek(frame:Fmallint);其中,Frame参数用于设置播放的帧序号。(4)Stop方法:终止播放操作。方法原型为:ProcedureStop;8.3.3媒体播放器控件媒体播放器控件是一个具有多媒体播放能力的窗口控件。它通过Windows操作系统的MCI接口直接控制各种类型媒体播放设备。按扭从左到右分别为:Play(播放)、Pause(暂停播放和录制)、Stop(停止播放和录制)、Next(跳到下一磁道,媒介不支持磁道时,跳到最后)、Prev(跳到前一磁道,媒介不支持磁道时,跳到最前)、Step(前进若干帧)、Back(返回若干帧)、Record(开始录制)、Eject(释放媒介)。1媒体播放器的属性(1)Device属性图8-10媒体播放器组件的形式TMPDeviceTypes类型指定进行播放的设备类型DeviceType属性的值设备类型播放的媒体文件类型dtAVIVideoAVI视频文件dtCDAudioCD唱盘dtDAT数字音频磁带dtDigitalVideoAVI、MPG、MOV文件dtMMMovieMM电影dtOverlay模拟视频dtScanner图像扫描设备dtSequencerMIDI文件dtVCRWAV文件dtAutoSelect默认值,依FileName制定的文件确定使用哪种播放设备(2)FileName属性FileName属性是字符串类型,指定要播放的多媒体文件。(3)媒体播放器状态的属性AutoOpen属性:布尔类型。其值为True时,程序运行自动打开指定的播放设备:为False时,各按钮工作状态由应用程序管理。Capabilities属性:TMPDevCaps类型。打开媒体播放设备后,应用程序可通过该属性了解播放设备的功能。其取值有:DtAVIVideo(当前播放设备可弹出媒介)、DtCDAudio(当前设备有媒介播放媒介能力)、DtDAT(当前设备有媒介录制能力)、DtDigitalVideo(当前设备有媒介前进、后退的能力)、DtMMMovie(当前设备需窗口输出)。Display属性:TWinControl类型。要在指定窗口播放时,可通过该属性(默认为Nil)为媒体播放器指定视频窗口,表示由播放设备创建一个窗口来输出视频信息。Mode属性:TMPModes类型。其指定当前多媒体设备的工作状态,取值有:mpNotReady(播放设备未准备好)、mpStopped(设备为停止状态)、mpPlaying(设备为播放状态)、mpRecording(设备为录制状态)、mpPouse(设备为暂停状态)和mpOpen(设备为打开状态)。(4)播放的属性VisibleButtons属性:TMPBtnType类型。确定媒体播放控件中显示哪几类按钮,其值为btPlay、btRecord、btStop、btNext、btPrev、btStep、btBack、btPause和btEject的组合。AutoEnable属性:布尔类型。其值为True时,在多媒体程序运行时,播放器按自身工作状态自动设置各按钮工作状态;为False时,各按钮工作状态由应用程序管理。AutoRewind属性:布尔类型。控制媒体播放器是否自动重播,其值为True时,重回媒体头部播放。TimeFoamat属性:因为媒体类型的多样性,需要以不同的方式来衡量媒体的播放量,如播放时间、播放帧数等。该属性用于定义媒体播放器组件的Start、Length、StartPos、EndPos属性的值,取值有:tfMilliSeconds(组件的Start、Length、StartPos、EndPos属性单位为毫秒)和tfFrames(组件的Start、Length、StartPos、EndPos属性单位为帧)。Frames属性:确定播放器组件的Step方法在前进或后退时移动的帧数。Position属性:确定媒体播放器在媒介的当前位置。(5)关于事件和方法的属性Notify属性:布尔类型。确定播放器组件是否产生OnNotify事件,其值为True时,任何操作都产生OnNotify事件,并将此次操作结果存储在组件NotifyValue属性中。NotifyVaule属性:TMPNotifyValue类型。指定播放器组件的方法,可能结果有:nvSuccessful(操作成功)、nvSuperseded(操作暂停)、nvAborted(操作被用户终止)和nvFailure(操作失败)。2媒体播放器的方法(1)Open方法:打开媒体播放设备。打开前要指定设备类型,操作完成后产生OnNotify事件。应用程序可检查组件的Error属性和ErrorMessage属性,以便得到操作成功或出错的类型。(2)Close方法:关闭已打开的媒体播放设备。(3)Eject方法:强制性打开媒体播放设备并释放装入的媒介。(4)Step、Back方法:指定媒体播放设备前进或后退帧数。帧数由组件的Frames属性确定。(5)Play方法:播放已打开设备中装入媒体信息。(6)Provious方法:使设备处于媒体的头部。(7)Save方法:将已装入的媒体信息存储到FileName确定的文件中。(8)Stop方法:停止录制和播放操作。(9)Pause方法:暂停录制或播放操作。第第9章章 Windows高级编程高级编程9.1动态链接库编程动态链接库编程9.1.1动态链接库简介1动态链接库(DLL)简介动态链接库(DymmicLinkLibrary简称DLL)是一些编译过的可执行程序模块,它包含代码,数据或资源,可以在应用程序或其他DLL中调用动态链接厍的文件扩展名一般为.dll,也可以是.drv(设备驱动程序)、.sys(系统文件)和.fon(字体文件),DLL可以实现多个应用程序共享代码和资源。2动态链接库(DLL)工作原理使用DLL的动态链接并不是将库代码拷贝,只是在程序中记录了函数的入口点和接口,在程序执行时才将库代码装入内存。所以不管多少程序使用了DLL,内存中都只有该DLL的一个副本。当没有程序使用它时,系统就将它移出内存,减少了对内存和磁盘的要求。动态连接库属于Windows可执行文件,但它又不是EXE文件,它不像EXE文件那样可以直接执行,DLL文件中包含的可执行代码是由EXE文件调用的。3动态链接库(DLL)特点DLL最大的特点就是它的代码在运行期间被动态地链接至调用它的程序中。它不用重复编译或链接,一旦装入内存,DLL函数可以被系统中的任何正在运行的应用程序所使用,他们共享该DLL函数的单一拷贝。DLL中一般由程序通用的过程、函数等构成,当然也可以包括各种资源。在创建Windows应用程序时,链接过程并不把DLL文件中的例程链接到程序上,只有当EXE文件运行并需要调用一个DLL文件中的函数或过程时,Windows才在DLL中寻找被调用函数并把它的地址传递给调用程序。9.1.2创建DLL通过Delphi主菜单的【File】|【New】|【Other】在弹出的【NewItems】对话框中,选择DLLWizard图标,单击【OK】按钮,系统将自动创建一个DLL项目。DLL和Application项目文件的格式对比DLL项目文件主要格式:Application主要格式:Library项目名;Program项目名;Uses子句;Uses子句;Exports子句;/数据接口函数Begin/程序执行体Begin/程序执行体End.End.DLL项目文件和一般项目文件主要存在两个方面的区别:(1)Application项目文件用Program关键字作程序头,而DLL项目文件用Library关键字作程序头,因此编译器会根据不同的关键字来生成不同的可执行文件。用Program关键字生成的是.exe文件,而Library关键字生成的是.dll文件。(2)第二个区别是DLL提供接口都是Exports关键字来实现的。在DLL项目文件中,将我们想要输出的函数或过程,列在Exports子句中,就可以实现输出了。1Exports关键字的使用用关键字Exports引出函数或过程,表明编译时要使用远程地址调用,使得函数或过程在DLL内可被其他模块访问。Exports有如下几种形式:(1)exports例程名;/名字引出(2)exports例程名index索引值;/索引值引出index用来指示为一个函数或过程分配一个顺序号,其值的范围是为132767。(3)exports例程名name别名;/别名引出name后面接的是字符串常量,用来指出该过程或函数的输出名。(4)exports例程名name别名resident/resident选项9.1.3DLL文件的静态调用DLL文件的调用一般有两种方法,即静态调用方法和动态调用方法,静态调用方式,就是在本单元的Interface部分用External指示字列出要从DLL中调用的过程;动态调用方式是通过调用WindowsAPI中的LoadLibrary函数、GetProcAdrress函数和FreeLibrary函数来实现DLL文件的动态调用。1静态调用静态调用又称为隐式加载方式,它是通过单元体中Interface部分的External指示字所列出需要调用DLL文件的过程或函数。这些被指定的DLL文件中的过程是在程序执行之前被加载进内存的。静态调用方式在使用DIL中的函数或过程之前,先引入DLL中的函数或过程。引入DLL中的例程一般有三种方法:(1)通过过程名或函数名来调用:functionfuncName(参数):DataType;tdcall;externalDLL文件名(2)通过过程或函数的别名来调用:functionfuncName(参数):DataType;stdcall;externalDLL文件名name别名(3)通过过程或函数的索引来调用:function funcName(参数):DataType;stdcall;externalDLL文件名indexn在Delphi7中,存在一个调用约定的问题。调用约定就是指调用例程时参数的传递顺序,在Delphi中DLL支持的调用约定如表所示。使用静态调用方式,有两个缺点:一是当要加载的DLL文件不存在或者没有指定调用的过程时,程序就会自动停止运行,或可能致使程序出错;二是一旦所需的DLL文件被加载至内存后,即使不再需要,其仍然停留在应用程序的地址空间中。调用约定调用约定参数传递顺序参数传递顺序Register从左到右pascal从左到右Stdcall从右到左Cdecl从右到左Safecall从右到左9.1.3DLL文件的动态调用1动态调用动态调用又称为显示加载方式,它可以解决静态调用中存在的局限性。动态调用不需要在单元体Interface部分中把需要调用的所有DLL过程都列出,其只要在调用前引用,并且使用LoadLibrary函数指定需要加载的DLL文件,使用GetProcAddress函数指定所调用的过程或函数,并返回该过程或函数的入口地址,使用FreeLibrary函数实现将该函数从内存中移除。此外,如果指定的DLL文件出错,不会导致程序终止运行。动态调用DLL主要用到三个Win32API函数:LoadLibrary()、FreeLibrary()、GetProcAddress()。(1)Loadlibrary():把指定库模块装入内存其使用的语法为:functionLoadlibrary(LibFileName:PChar):THandle;LibFileName指定了要装载DLL的文件名,如果LibFileName没有包含一个路径,则Windows按下述顺序进行查找:当前目录;Windows目录;Windows系统目录;包含当前任务可执行文件的目录。列在PATH环境变量中的目录以及网络的映象目录列表。如果函数执行成功,则返回装载库模块的实例句柄。(2)GetProcAddress:取得给定模块中函数的地址语法为:functionGetProcAddress(Module:THandle;ProcName:PChar):TFarProc;Module包含被调用的函数库模块的句柄,这个值由Loadlibrary返回。如果把Module设置为nil,则表示要引用当前模块。(3)Freelibrary():从内存中移出库模块语法为:procedureFreelibrary(Module:THandle);Module为库模块的句柄。这个值由Loadlibrary返回。由于库模块在内存中只装载一次,因而调用Freelibrary首先使库模块的引用计数减一。如果引用计数减为0,则卸出该模块。每调用一次Loadlibrary就应调用一次FreeLibray,以保证不会有多余的库模块在应用程序结束后仍留在内存中。9.2 ActiveX编程编程9.2.1创建ActiveX控件Button(1)从)从Delphi菜单中选择打开菜单中选择打开【File】|【New】|【Other】命令打开命令打开 【NewItems】对话框,翻到对话框,翻到ActiveX页,然页,然 后后 选选 择择 ActiveXControl图标图标。(2)单击OK按钮,显示ActiveX控制向导。在【VCLClassName】框内选择组件模板上的一个可视组件,然后在【NewActiveXName】框内输入要创建的ActiveX控件名,通常最后一个字母是大写的X。也可以接受缺省的控件名。在【VCLClassName】列表框中并没有包还所有的VCL控件,只有符合下面三个条件的VCL控件才会被列出来。一是该VCL控件必须已经安装到了组件面板上;二是该控件必须继承于TwinControl;最后一个条件是该控件没有调用过RegiserNonActiveX()。在【ImplementationUnit】中显示ActiveX实现的单元名称。在【ThreadingModel】中显示用于选择线程的模式,可以选择Apartment,free,single或both。(3)单击OK按钮。Delphi将创建下面的文件:ActiveX项目文件:ButtonXControl1.DPR类型库文件:ButtonXControl1.TLB类型库的接口源文件:ButtonXControl1_TLB.pasActiveX接口实现单元文件:ButtonImpl1.PAS9.2.2添加新属性要给接口增加属性或方法,需要用【View】|【TypeLibrary】|【TypeLibrary】编辑器,同时在IButtonX上单击鼠标右键,选择【New】|【Property】。单击该菜单项后,将弹出如图所示的对话框。图9-7 添加新属性示意图添加属性Mycaption,其Type(数据类型)选择BSTR。可以看到在左侧的树型列表框中增加了两个Mycaption。通过查看ButtonXControl1_TLB.pas文件,可以在IbuttonX接口的声明中看到这样的声明:propertyMycaption:WideStringreadGet_MycaptionwriteSet_Mycaption;其中Get_Mycaption和Set_Mycaption是属性Mycaption的访问方法。选择【Project】|【BuildButtonXControl1】命令,就可以生成ButtonXControl1.ocx文件。此时可选择【Run】|【RegisterActiveXServer】将它注册到系统注册表中。注册以后,就可以在【ImportActiveXControl】对话框中的组件表中看到它了。该对话框可以通过【component】|【importactivexcontrol】打开,如果没有先注册【ActiveX】控制,则在组件列表中看不到它,可以单击【Add.】按钮将其加进来。9.2.3ActiveX的使用在下列两种情况下考虑使用ActiveX控件:1没有合适的delphi组件可以使用时。2想使用多种编程语言进行开发,想在多个开发平台间共享一些控件时。把ActiveX控件安装到delphi组件面板的步骤如下:1点击菜单【component】|【importactivexcontrol】。2新窗口的上半部显示了在系统中已经注册过的ActiveX控件的列表。如果该ActiveX控件还没有注册,单击【ADD】,然后在某个文件夹中选择这个控件的OCX文件,单击【OPEN】,就完成注册了;3注册后,在窗口的下半部就可以安装已注册过的ActiveX控件。首先在窗口上半部的列表中选择该ActiveX控件,在下半部指定单元文件名、组件面板的页标签(默认是ACTIVEX页)、搜索路径以及封装OCX的类名,然后单击INSTALL或CREATE按纽,会打开INSTALL对话框,选择安装组件的包,这里可以选择一个已有的包来安装该控件,也可创建一个新的包来安装该控件。4单击OK,该ActiveX控件就安装到组件面板中了。第第10章章 Delphi数据库编程数据库编程10.1数据库系统开发基础数据库系统开发基础数据库系统主要由三大部分组成:(1)数据库(按一定结构组织在一起的相关数据的集合)。(2)数据库管理系统(DBMS:它是专门负责组织和管理数据信息的程序)。(3)数据库应用程序(它使用户能够获取、显示和更新DBMS存储的数据)。10.1.1数据库的基本概念1数据库(DataBase)数据库简单的定义是:有蕴含着一定的意义的数据,一些按照一定的规律组织起来所组成的数据集合。在数据库中除了用一些作为外部信息的数据之外,还有一些内部信息数据。这些数据定义了数据库的用户及其相应的权限,数据库表单的定义等等,通常把存放这些数据的地方叫做数据字典。数据字典是有数据库系统自行创建并自动维护的,它实际上也是数据库的一组表和视图,与其他的表单和试图并没有物理结构上的区别,唯一不同的是它的内容。2关系数据库(RelationalDataBase)关系数据库是由若干个表组成的,每一张二维表对应着一种联系。表的每一行称为记录;表的每一列叫做字段;域就是属性的取值范围。对于dBASE、FoxPro、Paradox这三种数据库系统,数据库对应于某一个子目录,而其他类型如MSAccess、Btrieve则是指某个文件。表(Table):一个表就是一组相关的数据按行排列,像一张表一样。字段(Field):在表中,每一列称为一个字段。每一个字段都用相应的描述信息,如数据类型、数据域等。记录(Record):在表中,每一行称为一条记录。索引(Index):为了加快访问数据库的速度、许多数据库都使用索引。主键(Key):主键是对于这张表的惟一标识,即一个列或几个列的组合。主键最显著的特点就是在任何给定的条件,没有两个主键包含相同的值,这个称作主键的惟一性原则。同时主键中每一个属性都不能被去掉,而同时仍能够保持主键的惟一性,这个我们称作主键的最小性原则。关系型数据库系统具有很多优点:(1)关系数据库有深厚的理论基础,它是基于关系代数和关系理论的模型。(2)以二维表的形式表示数据。(3)表与表之间的联系不是硬编码的。(4)不需要用户了解它在计算机中的物理存储形式。(5)用系统表来提供其本身的内容和结构。(6)可以通过SQL语言来操纵。SQL语言是专门用于操作这种模型的语言。(7)支持空值的概念。3数据库管理系统(DBMS)数据库管理系统是一个用来管理数据库的软件,是数据库能够正常工作的核心。对数据库的所有操作,包括创建各种数据库的数据类型、表单、视图、存储过程,以及其他的数据库应用程序对于数据库中数据的读取和修改,都是经由数据库管理系统完成的。当数据库应用程序把对于数据库数据的操作指令通过数据库管理系统的接口函数发送给数据库管理系统后的一切工作都只是数据库管理系统的的了,数据库应用程序所要做的就只是等候数据库管理系统把它所需要的数据给它,然后进行加工处理。4数据库应用程序数据库应用程序是通过DBMS访问数据库中的数据并向用户提供数据服务的程序。简单地说,它们是允许用户插入、删除和修改并报告数据库中数据的程序。这种程序是由程序员使用通用或者专用的程序设计软件开发的。10.1.2数据库设计过程1数据库的建立创建一个数据库的过程有以下几个步骤:(1)确定数据库的使用范围(2)确定支持数据库所需要的字段。(3)将字段划分成一些合理的数据表。(4)确定数据表之间的关联。在确定数据库的需求后,要将这些需求划分成几个合理的数据表。所谓合理的数据表,通常要满足以下几点:(1)数据表中的字段所描述的内容有一定的联系。(2)数据表中至少有一个字段的记录不是重复的。(3)一个数据表与数据库其他的数据表中至少一个能够关联。(4)一个数据表与数据库其他的同一数据表不要有多对多的关联。2数据表的结构在数据表结构中需要一个关键字段,数据表中的数据就是按主关键字段的顺序存放的,而且利用主关键字能够高效地与其他数据表建立关联。索引也是数据表常用的,在数据库中,利用索引可以加快访问速度。10.1.3数据库应用程序的开发步骤1初步设计设计阶段要根据用户的需求,定义数据库和应用程序的功能,确定用户的需求功能哪些在设计阶段实现,哪些在程序中实现。2功能实现将客户需求功能分成几个合理的功能块,分别进行程序设计、调试。常见的划分方法上分成四个功能块:(1)信息处理(2)数据库管理(3)系统维护(4)辅助功能信息处理是建立数据库应用程序的目的。设计数据库应用程序的目的是为客户提供所需要的信息服务,辅助管理工作,提高工作效率和水平。信息处理最基本的功能包括各类信息查询,统计报表等功能,对于特定的应用程序还可以有特定的功能。数据库管理的主要功能是负责数据库的更新、修改等。一个特定的数据库管理操作要由它的用户的权限决定,这个权限要由有权的用户指定。系统维护的功能是保证数据库应用程序运行的可靠性和安全性,一般包括用户管理,口令设置,各类系统变量和数据字典维护等。3运行和维护程序用户在使用应用程序的过程中会对应用程序提出一些建议和要求,根据用户的建议和要求对数据库应用程序进行适当的修改和完善,从而提高程序的性能。10.2 SQL结构化查询语言基础结构化查询语言基础10.2.1SQL语言的发展目前SQL语言被广泛地使用,它具有强大的生命力,它使用所有数据库用户包括程序员、DBA管理员和终端用户都受益非浅。SQL语言具有以下优点:(1)SQL语言是所有关系数据库的公共语言。(2)SQL是非过程化查询语言。有两种方式使用SQL语言:一种是在终端交互方式下使用,称为交互式SQL语言;另一种是嵌入在高级语言编写的程序中使用,称为嵌入式SQL语言。10.2.2SQL的基本查询功能SELECT语句是使用最多的SQL语句,它完成的是数据库的查询功能,SQLSELECT语句,从数据表中选择出符合条件的记录。Distinct语句的作用是对某个表所选择的字段数据,忽略重复的情况,也就是说,针对某个字段查询出来的记录结果是惟一的。1TOP和ORDERBY语句TOP语句实现从第一条或最后一条开始(利用ORDERBY条件子句),返回特定记录的数据的功能。如果没有加上ORDERBY条件的话,所得到的数据,将会是随机的数据。此外,在TOP语句之后,除了可以加上数字以外,还可以利用保留字PERCENT来查询。排序参数:ASC递增顺序排列,默认值。DESC递减顺序控制。2IN条件字句指定要查询哪一个外部数据库的表。3HAVING条件子句指定一特定的分组记录,并满足HAVING所指定的条件或状态,但条件是针对分组的条件设置。4GROUPBY条件子句依据指定的字段,将具有相同数值的记录合并成一条。5BETWEEDAND运算符决定某一数值是否介于特定的范围之内。6LIKE操作数将一字符串与另一特定字符串样式比较,并将符合该字符串样式的记录过滤出来。10.2.3SQL的其他应用1SQL数字函数(1)AVG:算数平均数。(2)COUNT:计算记录条数。(3)FIRST与LAST:返回某字段的第一条数据与最后一条数据。(4)MAX与MIN:返回某字段最大值与最小值。(5)SUM:返回某特定字段或是运算的总和数值。2SQL查询的嵌套嵌套的SQL查询含义在于:在一个SQL语句中可以包含另一个SQL查询语句,形成内部嵌套的查询类型,SELECT语句构成的多层SQL查询,必须用()将该语句括起来。3SQL与数据库的维护(1)表的建立CREATETABLE语句:我们可以利用这个命令,来建立一个全新的表,但前提则是数据库必须已经存在。(2)表索引的建立CREATEINDEX语句:这个命令主要是对一个已经存在的表建立索引。(3)表的删除DELETE语句:我们可以利用DELETE语句,将表中的记录删除。(4)SELECTINTO语句:可以通过这个命令,利用已经存在的表查询,来建立一个新表。(5)INNERJOIN操作数:当某一个共同的字段数据相等时,将两个表的记录加以组合。(6)UNION操作数:可以通过UNION操作数来建立连接的查询条件,UNION操作数可以将两个以上的表或是查询的结果组合起来。(7)ALTER语句:在一个表建立后,利用ALTER语句,可以去修改表的字段设计。(8)DROP语句:针对所指定的表或字段加以删除,或是把索引删除。(9)INSERTINTO语句:新建一条数据到表当中。(10)UPDATE语句:建立一个UPDATE的查询,通过条件的限制来修改特定的数据。10.3数据库应用程序的结构与设计数据库应用程序的结构与设计数据库应用程序的功能是:使用数据访问组件,通过数据引擎(提供数据库驱动程序),建立与数据源(数据库)的连接,并使用数据控制组件来创建用户界面,以便用户存取和操纵数据库中的数据。10.3.1应用程序的结构在数据应用程序中,通常包含3种数据库组件1数据集组件访问数据库,通常需要数据源,用DataSource组件描述数据源。但该组件不能直接表示数据,而是引用数据库表、查询结果或存储过程。在窗体中还需要使用Table、Query或StoredProc组件,它们直接与数据库连接,从中获取数据。常称之为数据集组件,它们通过BDE为应用程序提供与数据库的连接。当要创建一个数据库应用程序时,先在窗体上放一个数据集组件,然后为数据集组件设置有关的属性,指定要访问的数据库、数据表以及表中的记录等。2数据控制组件数据控制组件为用户提供一个可对数据库中数据进行浏览、编辑和输入等数据操作的可视化界面,由于数据集组件是不可见的,所以,还必须使用可见的数据控制组件来提供数据库的显示,它们通过数据访问组件DataSource相互连接,数据控制组件也称为数据感知(Data-aware)组件。3数据访问组件数据访问组件DataSource负责双方数据的收发。使用户交互式地对数据库进行查询、修改、插入和删除等操作。在同一个窗体上几个数据控制组件可以连接到同一个DataSource组件,这几个数据控制组件可以保持同步,因为数据控制组件总是显示当前数据记录的数据。DataSource组件最好放在数据模块上,与用户界面分开。DataSource组件几种重要属性的意义如下:(1)DataSet属性:指定相连的数据集组件。(2)State属性:表示连接的底层数据集状态。(3)AutoEdit属性:确定连接的数据集组件是否自动处于编辑状态。10.4基于基于BDE与与ODBC的数据库连接的数据库连接10.4.1BDE简介Delphi的Borland数据引擎(BDE)可直接访问某些本地数据库,包括dBASE,Paradox,FoxPro,MicrosoftAccess,Delphi安装程序自动为这些数据库安装了驱动程序,并建立了相应的配置。如果将BDE与Borland的SQLLink驱动程序连接,可以访问Oracle,Sybase,Informix,InterBase和IBMDB2数据库。如果要连接的是BDE所不支持的数据库,则可利用BDE所提供的ODBC管道,通过ODBC来访问它们。10.4.2配置BDE数据源1BDE管理器BDE管理器使管理Windows注册表中的BDE系统配置信息和BDE配置文件(IDAPI.cfg)中的别名信息的可视化工具。选择菜单项【开始】|【程序】|【BorlandDelphi7】|【BDEAdministrator】,即打开BDE管理器BDE管理器的Configuration页用于显示和配置相关数据库的驱动程序,这些驱动程序可分为两类:Native驱动和ODBC驱动。在Configuration窗格的驱动程序列表中选择一个驱动程序,它的设置参数便会显示在Definition窗格中,以便查看和修改。2数据库别名在实际应用中,计算机尤其是文件服务器会经常更换。相应地,各种数据库操作也要转移到新数据库服务器上进行,因而需要对数据库重新定位。BDE通过数据库别名(数据源)解决这个问题。在数据库应用程序中,当数据从一个地方转移到另一个地方时,只要修改别名重新定位数据库位置即可,源程序不必修改。3配置数据库驱动程序在BDE管理器Configuration页中,左窗格(Configuration)显示驱动程序列表,其中Native驱动和ODBC驱动分列为两项,右窗格(Difinition)显示当前驱动程序的配置。Native驱动和ODBC驱动配置方法略不同。(1)Native列表中列出了已有的Native驱动程序名称,可以对其中的驱动程序进行配置。不同驱动程序的可配置项目有所有同。(2)ODBC列表中列出了已有的ODBC驱动程序名称,可以添加新的ODBC驱动程序。当然也可以对其中的驱动程序进行配置。配置好了驱动程序之后,要选择菜单项【Object】|【Apply】,将配置保存起来。然后切换到Databases页,即可使用配置好的驱动程序来建立数据源。4建立BDE数据源(1)右击Databases页,选择快捷菜单New项。(2)在DatabseDriverName下拉列表中选择一种数据库驱动程序,单击OK按钮,生成一个数据源。如果选择的是STANDARD,则自动生成名为Standard1的数据源,并显示在Databases页的树形数据库别名表中。(3)根据需要修改数据源的名称,配置右侧Definition页中的参数值。(4)右击刚创建的数据库别名,选择快捷菜单Apply项,按提示保存数据库别名配置。10.4.3建立ODBC数据源以Access数据库为例说明创建ODBC数据源的方法。1打开ODBC数据源管理器在BDE管理器窗口中,选择Object菜单的ODBCAdministrator项,或使用Windows控制面板中的ODBC对象,打开ODBC数据源管理器窗口。为了便于访问数据,Windows系统提供了ODBC数据源管理工具,该工具用来设置数据源的名字DSN(DataSourceName)。DSN是一个数据源的标志,目的是便于应用程序访问数据,即只要某个数据设置了相应的DSN,应用程序就不必理会该数据库存储的位置和驱动程序,可以按DSN直接访问数据库。DSN有三种类型:(1)用户DSN:只对设置它的用户可见,而且只能在设置了该DSN的机器上使用。(2)系统DSN:对机器上的所有用户都是可见的,包括NT服务。(3)文件DSN:将DSN的配置信息存在一个文件里,这样的文件就叫文件DSN。2选择【用户DSN】选项卡,单击【添加】按钮,打开创建数据源对话框。选择MicrosoftAccessDriver(*.mdb),并单击【完成】按钮。3在弹出的对话框中设置数据源名为Huoyun,通过【选择】按钮选择数据库,或通过【创建】按钮创建新数据库,还可以修复和压缩数据库,还可通过“高级”按钮设置登录名称和密码。点【选择】按钮,选择一个已经存在的Access数据库D:MyDocumentsHY.mdbs数据库。4输入数据源的名称“huoyun”,说明部分输入一些对所创建的数据源进行描述的文字,也可不输入,设置之后,单击【确定】按钮保存设置这样就配置好了DSN,关闭ODBC数据源管理器。关闭所有使用BDE的应用程序,打开BDE管理器窗口,切换到DataBase页,即可看到新建的ODBC数据源“huoyun”。10.4.4使用数据库浏览器DatabaseExplorer的功能是定义数据库,装载数据库,管理数据库,还可以直接执行SQL语句。1打开数据库浏览器窗口选择菜单项【开始】【程序】【BorlandDelphi7】【SQLExplorer】,或在Delphi集成开发环境的主窗口中选择菜单项【Database】【Explore】,都可以打开SQLExplore窗口在SQLExplorer窗口的Databases页,左(Databases)窗格显示所有的数据库别名列表,右(Definition)窗格显示当前数据库别名的配置。本例中,需要给数据库浏览器指定要操作MicrosoftAccess示例数据库HY.mdb。方法如下:(1)在左窗格选定已建立的数据库别名HYDB。(2)在右窗格修改HYDB的配置,利用DATABASENAME格的按钮,将该属性设置为d:MyDocmentsHY.mdbB。(3)在右窗格修改ODBCDSN的配置,选择下拉框Huoyun选项。(4)选择Object菜单Apply项,保存所做设置。2打开数据集在数据库浏览器中打开数据集步骤如下:(1)在SQLExplorer窗口Databases页左窗格中,选定数据库别名HYDB。(2)选择Object菜单的Open项,弹出数据库连接(DatabaseLogin)对话框。单击OK按钮,即可打开HY.mdb数据库。(3)单击HYDB项左侧的号,展开它,则HY.mdb数据库中的数据库表对象、查询对象都会显示出来。(4)选定“报价”表,并在右侧切换到Data窗格,“报价”表内容在Data窗格显示出来。3操纵数据库(1)浏览数据。选定整个记录:数据表最左边有一列用于选定记录的按钮,单击某个即选定它所在的记录;可利用垂直滚动条配合操作。另外,工具栏右半部是与DBNavigator控件相仿的如下图所示查找工具栏,可用于移动记录指针或进行插入、删除等操作。选定整个字段:单击字段名即可选定一个字段,可利用水平滚动条配合操作。选定指定记录的指定字段:数据集中的数据是以网格形式显示的,单击某个网格,即可选定它。可利用垂直滚动条和水平滚动条配合操作。浏览备注型字段(以Memo标记):备注型字段内容是一些长度差别较大的字符串。单击某个记录中备注型字段的网格时,会弹出一个窗口,显示它的内容(文字或空白)。浏览图片字段(以Graphics标记):单击图片字段所在的网格,就会弹出一个窗口,显示该字段的内容(图片或空白)。(2)利用数据库浏览器也可对数据集进行编辑、插入、删除、复制和移动等操作。编辑操作:选定要编辑记录,单击EditRecord()按钮切换到编辑状态,然后修改各字段的值。编辑结束后,单击CancelEdit()按钮或选定其他记录退出编辑状态。插入操作:选定一个记录,单击InsertRecord()按钮,则所选记录之前插入一个空记录(带有*符号)。输入一个记录的内容之后,单击PostEdit再插入一个记录,或单击CancelEdit退出插入状态。删除记录:选定要删除的记录,单击DeleteRecord()按钮,则弹出一个消息框提问是否真要删除。单击OK按钮即可删除记录,单击Cancel按钮不删除记录。4使用SQL语句执行查询使用SQL查询数据库的步骤如下:(1)在左窗格中选择一个数据库对象(表、查询等)。(2)在右窗格中,切换到EnterSQL页,在编辑框中输入一个SQL语句。(3)单击executequery按钮,执行SQL语句。10.5 数据库操纵数据库操纵 对数据库的存取和控制称为数据库操纵,包括数据的查询、读取、插入、删除和更新等各种操作。既可利用Delphi本身的功能进行数据库操纵,也可使用通用数据库的结构化查询语言(SQL)来进行。10.5.1字段的操作Delphi用字段对象(TField组件)来表示数据集中的字段。字段对象附属于数据集组件,在程序设计和运行过程中都是不可见的。每当数据集组件从数据库中获得数据时,就将其(当前记录)放入字段对象中。利用字段对象可取得当前字段的值,设置它的值,而且能够通过修改字段对象的属性来改变数据集。字段对象的创建有两种方式:(1)在应用程序打开数据集时自动创建。将数据集组件的Active属性设为True,或执行它们的Open方法,即可创建字段对象。这种方式称为动态创建。这样创建的字段对象在单元文件中没有声明语句。当数据集关闭,即当数据集组件的Active属性设为False或执行它们的Close方法时,便会自动撤消。(2)通过字段编辑器创建字段对象。这种方式创建的字段对象称为静态字段对象,在关闭数据集时不会撤消。1字段的数据类型一个字段对象用来表示数据集中一列的特征,也用于表示字段的显示特征,另外,当在数据集的记录之间滚动时,字段对象将更新当前字段的值,并显示给用户查看。数据集中的字段有多种数据类型,每种数据类型都有一个独立的TField类型与之对应。常用的TField类型有:TBooleanField(布尔型)、TCurrencyField(货币型)、TStringField(字符串型)、TIntegerField(整数)、TFloatField(浮点型)和TBLOB(二进制对象)。使用最多的是TStringField和TIntegerField类型的字段对象。2字段对象的访问(1)数据集组件有一个默认的数组属性FieldValues,这个数组以Variant类型返回字段的值,利用它可以存取字段值,利用它可以存取字段值。(2)利用数据集组件的数组属性Fields也可以存取字段的值。Fields属性的每个元素都代表一个字段,这样就可以按序号(从0开始)来访问字段的值。(3)利用数据集组件的FieldByName方法,通过列名也可访问字段对象。FieldByName方法以数据集的字段名作为参数(用引号)。(4)编辑当前记录中一个字段的操作步骤如下:调用数据集组件的Edit方法,使数据集处于编辑状态。给当前字段赋新值。调用Post方法将数据的变化提交给数据集。(5)在数据集中插入或删除记录时需以下操作。调insert或append方法,使数据集处于相应模式。对数据集中的字段赋值。调用方法将数据的变化提交给数据。3.创建静态字段创建静态对象要用到字段编辑器。(1)双击Table组件,打开字段编辑器。(2)右击字段编辑器,选择ADDFields项,弹出添加字段对话框。(3)在添加字段对话框中选定要创建静态字段对象的字段,单击OK按钮,将选定的字段添加到字段编辑器中。4访问静态字段选择要设置属性的字段,在对象观察器中修改字段对象的属性。1Alignment:设置字段在数据控制组件中的对齐方式,左对齐、右对齐和居中。2DisplayLabel:说明字段对象咱DBGrid组件中的显示的标题,默认为字段对象名称。3DisplayWidth:字段对象在DBGrid组件中显示的列的宽度。4FileName:字段对象对应的字段名称。5Index:字段对象在DataSet组件中的索引号,从0开始。6Name:字段对象名。7ReadOnly:相应字段是否能修改。8Visible:相应字段是否在DBGrid中显示。9EditMask:设置掩码编辑格式。10MaxValue和MinValue:设置字段最大最小值,超范围时产生EdatabaseError异常。静态字段对象的访问相对于动态字段对象来要简单得多,在程序中可以直接通过字段对象的名称(即Name属性)来访问。10.5.2使用Tabel组件的记录查找在数据集中检索记录中有几种方式:一是使用Locate函数或Lookup函数进行检索;二是使用GotoKey方法或FindKey方法,基于索引关键字进行检索;三是使用GotoNearest方法或FindNearrest方法,执行非精确匹配检索。1使用Locate方法的通用查找Table组件的Locate方法和Lookup方法可以在任何数据表中按任何类型的字段来搜索记录,数据表不必建立索引。Locate方法用来在数据表中搜索一条符件条件的记录,如果找到的话,该记录即可成为当前记录。在使用Locate方法执行查询之前,先要调用SetKey方法,将连接表的Table组件设置成查询状态。2使用GotoKey方法的索引查找able组件的GotoKey方法可以基于索引中的字段搜索匹配的记录,并使找到的记录成为当前记录。使用GotoKey方法查找步骤如下(1)确保待查字段是关键字段(索引中的字段)或已经为它定义了辅助索引。(2)调用SetKey方法,将连接表的Table组件设置成为查询状态。(3)为每个待查字段设置目标值。(4)调用GotoKey方法实现查询,并测试它的返回值判断查询是否成功。3使用FindKey方法的索引查找将设置查找状态、设置查找值,以及执行查找集中在一个方法调用中实现。FindKey方法接受的参数是放在方括号中的,是用逗号分开的查找值数组。数组中的每个值都对应于特定字段的查找值,即参数中允许有多个查找值,FindKey允许用户同时查找数据集中的多个字段。4使用GotoNearest和FindNearest方法的近似查找不要求查找结果与查找值精确匹配。如果找到与指定值匹配的记录,则将记录指针到该记录处,否则,将会出与指定值最接近的记录并将记录指针指向它。10.5.3使用Query组件的SQL查询1连接数据库表使用Query组件构造数据库应用程序需要一个DataSource组件来和数据控制组件相连,而且也要通过DataBaseName属性指定数据库名称或目录路径。Query组件和Table组件的不同之处在于,它没有TableName属性,而是用SQL属性编写语句和某个数据集相连并相连并选择要显示的域。2SQL命令文本的编写(1)使用字符串列表编辑器编写利用Query组件的SQL属性打开字符串列表编辑器,键入SQL语句之后,单击OK按钮可将编辑器中的SQL命令文本装入SQL属性中。也可右击编辑器,选择Save项,将编好的SQL命令保存到一个文件中供使用。打开字符串列表编辑器之后,还可右击编辑器,选择快捷菜单的Load项,从一个SQL命令文件中调入SQL命令。(2)使用SQL构造器(SQLBuilder)可用于编写Select语句。右击Query组件,选择快捷菜单的SQLBuilder项,弹出SQLBuilder对话框。SQL构造器的上半部用于选择数据、数据表,以及表中的字段等;下半部用于选择性地构造查询条件。在SQL构造器中组织好一个查询并退出构造器时,其中的SQL命令会自动写入相应SQLBuilder组件的SQL属性。3静态查询SQL语句有两种:静态SQL语句和动态SQL语句。静态SQL语句在程序设计阶段就已经固定了。而动态SQL语句则在语句中加入一些参数,在程序运行过程中,可以改变参数的值,即可以动态地给SQL语句中的参数赋值。4动态查询对于较复杂的查询,一般采用动态查询,即在程序运行期可以改变查询条件的查询。动态SQL语句就是参数化的语句,即在SQL语句中包含着表示字段名或表名的参数动态SQL语句的编写方式可通过参数编辑器赋值。方法是:在对象观察器中,找到Query组件的Parames属性,打开参数编辑器。在其中的参数列表中选择一个参数,对象观察器便会显示参数属性。其中,DataType属性表示参数数据类型,ParamType属性表示参数使用类型,Value属性具有参数的值(Value)和类型(Type)两个子属性。设置了参数之后,关闭参数编辑器,打开Query组件所连接的数据库,则在与Query组件相连接的数据控制组件中会显示查询结果还可在应用程序运行过程中为参数赋值,这是使用可变参数的主要方法,这种赋值方法分为以下3种情况:(1)使用Parems属性按序号访问参数Query组件的Params属性在设计时不可有,在程序运行过程中可用而且是动态建立的。在为Query组件编写动态SQL语句时,Delphi自动建立一个数组Params,数组从0下标开始,依次对应SQL语句中的参数,即命令中第一个参数对应Params0,第二个参数对应Params1,依此类推。(2)使用ParemByName函数按名称访问参数ParemByName是一个函数,用动态SQL语句中的参数作为调用ParemByName函数的参数,这样便可为其赋值。(3)使用DataSource属性从另一个数据集获得参数使用上述两种方法的前提是用户预先知道具体的参数值,而在有些程序中,参数值是无法确定的。这就需要设置Query组件的DataSource属性值,其值为另一个DataSource组件的名字。10.6 基于基于ADO的数据库应用程序的数据库应用程序10.6.1ADO组件ADO组件(Delphi中称为dbGo组件)位于Delphi组件面板上的ADO页如图所示,这些组件可用于连接ADO数据存储、执行命令、检索基于ADO机制的数据库表中的数据。多数ADO组件都能在按其功能在BDE组件面板中找到相应的组件。图10-24 ADO组件页1ADOConnectionADOConnection组件位于ADO组件板的第一个组件,该组件用于与数据库建立连接。TADOConnection组件中还有两个属性:CommandTimeout和ConnectionTimeout。这两个属性主要用于连接远程数据库时相关参数的设定。ADOConnection组件和BDE组件板中的Database组件功能相似。2ADOCommandADOCommand组件位于ADO组件板的第二个组件,该组件有CommandType与CommandText两个重要属性。3ADODataSetADODataSet组件位于ADO组件板的第三个组件,通过ADODataset组件,可以直接与一个表进行连接,也可以执行SQL语句,还可以执行存储过程。在使用时,首先设定其Connection属性为ADOConnection组件,没有ADOConnection组件就直接设定ConnectionString属性。接着有两个重要属性CommandType与CommandText需要设定。4ADOQueryADOQuery组件位于ADO组件板的第四个组件,提供数据库连接接口一致化。5ADOTableADOTable组件位于ADO组件板的第五个组件,如果程序中使用ADOConnection连接组件,直接设定该组件的Connection属性为ADOConnection组件即可。另外一个重要属性就是Active属性,该属性用来设置打开或关闭与该组件相连数据表。其值若为True,则打开数据表;若为False,则关闭数据表。ADOTable组件与BDE中的Table组件对应。6ADOStoredProcADOStoredProc组件位于ADO组件板的第六个组件,用于完成数据存储过程。ADOStoredProc组件与BDE中的组件StoredProc对应。10.6.2通过ADO连接数据库1构造连接字符串(1)在对象观察中选择数据集组件的ConnectionString属性,单击属性项右侧的符号按钮,打开连接字符串对话框。(2)连接字符串对话框中有两个单选项,提供了两种不同的连接方式。使用数据链接文件:数据链接文件是一个扩展名为.UDL的文件,其中存放了一个连接字符,用户可以预先建立数据链接文件,以便连接字符串能够重复利用。使用连接字符串:选择了该项就需要自己创建一个连接字符串。可以在相应的文本框中键入一个连接字符串,但一般来说,还要进行数据引擎的选择,所以,应单击Build按钮,打开数据链接属性对话框。(3)选择数据库引擎。在对话框的Provider页,选择要连接的OLEDB提供者。(4)选择了数据提供者之后,可以单击Next按钮或单击Connection页标签,切换到Connection页,该页会按前面选择的数据提供者的不同而显示不同的设置项。2连接本地数据库10.7 人力资源管理系统开发人力资源管理系统开发10.7.1需求分析人事管理系统就是要实现对某单位的职工进行管理,整个系统包括人事资料录入、资料查询、资料删除等功能。经分析后,本系统具有以下3个功能模块:1系统功能(1)用户管理模块。该模块主要是实现操作用户的增加、删除和修改。(2)密码修改模块。该模块主要实现各操作用户修改自己的操作密码,系统管理用户可以修改其他用户的密码。(3)系统初始化模块。主要用来实现初始化功能。(4)退出模块。就是退出系统。2人事管理主要实现人事信息的增加、修改和删除等功能。3人事查询实现按各种条件进行查询,并且实现打印查询结果。10.7.2数据库分析为了人事管理系统正常运行,需要创建两个数据表:一个是操作用户数据表Operator表;另一个是人事信息数据表Info表。10.7.3数据库与数据源创建1BDE方式(1)选择【开始】|【程序】|【BorlandDelphi7】|【BDEAdministrator】,即可打开BDE管理器。(2)单击BDE管理界面的下拉菜单中【Object】|【New】选项,开始创建一个新的BDE数据别名,首先要选择数据库驱动程序名,因为选择采用Paradox数据库格式,所以选择STANDARD驱动。(3)单击【OK】按钮,就增加了一个默认名为STANDARD1的设置项。(4)修改Paradox驱动设置。单击【Configuration】页进行数据库驱动设置,单击【Native】|【Paradox】,出现Paradox数据表驱动的配置项,修改【LangDriver】中的值。(5)已经设置好了数据库别名,要使该别名立即生效,单击【Object】项中的Apply,这样别名就设置完毕。(6)接下来要创建Operator数据表,选择【开始】|【程序】|【BorlandDelphi7】|【DatabaseDesktop】,即可打开数据表管理窗口。(7)单击【File】|【New】|【Table】选项,出现数据表选择对话框,选择Paradox7。(8)单击【OK】按钮,就进入了创建数据表的对话框。输入字段信息。(9)单击【SaveAs】按钮,将保存已创建的数据表结构,这时提示输入数据表名。可以通过Alias选择已定义的别名,并且在文件名栏中输入要命名的文件名。(10)单击【保存】按钮即可,可以通过点击DatabaseDesktop数据库管理器中【File】|【open】|【Table】可以浏览刚创建的空的数据表。2ODBC方式(1)选择【开始】|【程序】|【Microsoftoffice2003】|【MicrosoftofficeAccess2003】,即可打开Access数据库管理系统。(2)选择【空数据库】,然后创建一个数据库,出现一个窗口。(3)双击【使用设计器创建表】创建Operator表。(4)选择【文件】|【保存】。(5)在表名称中输入Operator单击【确定】,即在数据库RLMIS中创建好了Operator表。10.7.4系统代码实现1程序主窗体设计(1)界面设计创建一个新的工程文件,在窗体中添加一个MainMenu组件,一个ToolBar组件,在ToolBar组件中添加六个ToolButton组件,一个Imagelist组件,双击Imagelist组件添加六个图片。(2)属性设置(3)程序设计2操作员管理设计(1)界面设计在操作员界面添加一个ToolBar组件,两个SpeedButton组件,3个GroupBox组件,在GroupBox1组件中添加4个Label组件和4个Edit组件,在GroupBox2组件中添加3个CheckBox组件,在GroupBox3中添加3个RadioButton组件。(2)程序设计3修改密码设计(1)界面设计在窗体中加入3个Label组件和3个Edit组件,一个ToolBar组件,在ToolBar组件上添加两个SpeedButton组件。(2)程序设计4系统初始化设计5人事信息设计第第11章章 网络编程技术网络编程技术11.1 概述概述WinSock是网络编程接口,而不是协议,它构成了Windows平台下进行网络编程的基础。Delphi中各种网络组件的强大功能,都是建立在WinSockAPI基础之上的。11.2 WinSock基础基础11.2.1 TCP、UDP和IP协议对应于OSI七层模型,TCP和UDP协议是位于传输层的协议,而IP则位于网络层。如图所示:OSI模型和网际协议族TCP是传输控制协议,它是一种面向连接的协议,它向用户提供可靠的全双工的字节流。TCP关心确认、超时和重传等具体细节。大多数Internet应用程序使用TCP,因为它是一种精致的、可靠的字节流协议。应用层表示层会话层传输层网络层数据链路层物理层应用层TCPUDPIP驱动程序和硬件UDP是用户数据报协议,它是一种无连接协议。UDP是一种简单的,不可靠的数据报协议,与TCP不同,UDP不能保证每一个UDP数据报可以到达目的。IP是网际协议。Ipv4(我们通常就称之为IP)自80年代早期以来一直是网际协议族的主力协议,它使用32位地址,为TCP、UDP、ICMP和IGMP提供递送分组的服务。90年代中期,又设计出了用以替代Ipv4的Ipv6,它使用128位的大地址。11.2.2套接口(Socket)和WinsockAPI套接口(Socket)最初是由加利福尼亚大学Berkeley学院为UNIX操作系统开发的网络通信编程接口。90年代初,由Microsoft、SunMicrosystems等几家公司共同参与制定了一套标准,即WindowsSockets规范。1993年,他们制定了WindowsSockets1.1规范,定义了16位Windows平台下的网络标准编程接口。对应于OSI七层参考模型,WinSockAPI是位于会话层和传输层之间的,它提供了一种可为指定的传输协议(如TCP、UDP)打开、进行和关闭会话的能力。WindowsSockets规范已经成为在Windows平台上开发网络应用程序的已接受标准,它也为想开发网络程序的程序员提供了巨大的方便。11.2.3面向连接和无连接面向连接发服务中,进行数据交换之前,通信双方必须建立一条用以进行通讯的路径。这样既确定了通讯双方之间的联系,又可以保证双方都处于活动状态,可以彼此响应。多数情况下,面向连接的协议可以保证传输数据的可靠性。TCP协议即是一种面向连接的协议。应用程序利用TCP进行通讯时,发起方和接受方之间会建立一个虚拟连接,通过这一连接,双方可以把数据当作一个双向的字节流来进行交换。无连接服务不管目的方是否处于待接收状态,源方只管将信息发送给目的方,也不管目的方是否已经接受到了该信息,以及接收到的信息是否无误。UDP协议即是一种无连接协议,数据传输方法采用的是数据报。面向连接服务和无连接服务各有优缺点。面向连接的服务能够保证通讯双方传递数据的正确性,但它却要为此进行额外的校验,同时,通讯双方建立通信信道也需要许多系统开销。无连接服务最大的优点就是速度快,因为它不用去验证数据完整性,也不为数据是否已经被接收操心。11.2.4客户/服务器模式当今网络应用中,通信双方最常见的交互模式便是客户/服务器模式。在这种模式中,客户向服务器发出服务请求,服务器收到请求后为客户提供相应的服务。客户/服务器模式通常采用监听/连接方式实现。一个服务器端的应用程序通常在一个端口监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务提出了连接请求。此时,服务进行被“唤醒”并且为客户提供服务,即对客户的请求作出适当的反应。11.2.5套接口类型在使用TCP/IP协议时,可选的套接口类型有三种:流式套接口、数据报套接口及原始套接口。流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。对于建立在这种类型套接口上的套接字来说,数据是可以双向传输的字节流,无长度限制。数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错。即一个建立在数据报套接口上的套接字所接收的信息有可能重复,或者和发出时的顺序不同。原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新网络协议实现的测试。目前,只有WinSock2提供了对它的支持。在Windows平台下,笔者也只能在Windows2000下使用过原始套接口。11.2.6使用面向连接的协议时套接口的调用客户/服务器模式的网络应用程序通常使用面向连接的协议。采用面向连接的协议(如TCP)时,服务器处理请求往往比较复杂,不是一来一去的简单请求应答所能解决的,往往要经过反复的交互。大多数TCP服务器是并发服务器。调用closesocket()关闭监听套接字s,停止服务交换数据连接请求调用socket(),建立流式套接字S调用bind()将套接字s与本地地址、端口绑定定定调用listen()通知底层协议(TCP),服务器已经准备好接收数据调用accept(),准备接受客户端的连接连接建立,accept()调用返回,得到新的数据套接字ns,ns只能用于数据传输;原套接字s仍然打开,处于监听连接状态调用recv()/send()在套接字ns上读写数据,直到数据交换完成调用closesocket()关闭数据套接字ns调用socket(),建立流式套接字c调用conncet(),使套接字c与远程主机建立连接调用recv()/send(),在套接字c上读写数据,直到数据交换完成调用closesocket()关闭监听套接字c,结束此次连接会话客户端服务器端图11-2 面向连接协议的典型套接口调用流程面向连接的服务器端首先调用socket()函数建立一个套接字s,用来监听客户端的连接请求。接着,调用bind()函数将此套接字与本地地址、端口绑定起来。之后,调用listen()函数告诉套接字s,对进来的连接进行监听并确认连接请求,于是s被置于被动的监听模式。一个正在进行监听的套接字将给每个请求发送一个确定信息,告诉发送者主机已经收到连接请求。但是监听套接字s实际上并不接受连接请求,在客户端请求被接受后,调用的accept()函数将返回一个与s具有相同属性,但不能被用来进行监听(即不能用来接受更多连接),只能用来进行数据收发的数据套接字ns,做为与客户端套接字相对应的连接的另一个端点。对于该客户端套接字后继的所有操作,都应该通过ns来完成。监听套接字s将仍然用于接受其他客户端的连接,而且仍处于监听模式。11.2.7使用无连接的协议进套接口的调用采用无连接协议(如UDP)时,服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的互相作用。大多数UDP服务器是迭代的。无连接的服务器端使用socket()和bind()函数来建立和绑定套接字s。只需调用recvfrom()函数在套接字s上等待接受数据。因为是无连接的,因此网络上任何一台机器发送给数据套接字s的数据报都可以被收到,它们是无序的。图11-3无连接协议的典型套接口调用流程交换数据调用socket(),建立数据报套接字s调用bind(), 将套接字s与本地地址、端口绑定调用recvfrom()/sendto(),通过套接字s读写数据,进行数据处理调用closesocket(),关闭套接字s,终止服务调用socket(), 建立数据报套接字c调用sendto()/secvfrom(),通过套接字c读写数据 调用closesocket(),关闭套接字c,结束此次会话客户端服务器端无连接客户端更为简单,只需要调用socket()建立一个套接字c,就可以用sendto()和recvfrom()函数进行与服务器端的数据交换了。在完成会话后,调用closesocket()函数关闭套接字c。11.3 网络聊天程序的实现网络聊天程序的实现11.3.1使用TCP协议由于网络的不稳定及经常丢失数等原因,在Internet上实现两台机器间的联系需要使用TCP。基于TCP/IP协议的传输是面向连接的点到点的传输。在聊天时,用户首先必须设置本地机器的端口号,打开本地TCP服务器,然后向远程主机发送连接请求,建立虚路连接,才能开始数据的传送与接受。介绍TIdTCPClient、TIdTCPServer组件的用法1TIdTCPClient组件TIdTCPClient组件位于IndyClients组件板如图所示,TIdTCPClient组件客户方通信管理,封装了完整的Socket。TIdTCPClient组件是许多Indy客户端组件的基类。(1)TIdTCPClient组件主要属性介绍BoundIP属性:用来指定客户端连接的本地IP地址图11-4 IdTCPClient组件BoundPort属性:用来指定客户端连接的本地IP地址,在Connect方法中指定绑定端口号Host属性:用于标识远程计算机地址,可以是IP地址,也可以是计算机名Port属性:用于表示远程计算机端口,和Host属性一起用于指明远程计算机地址ReadTimeout属性:用来指明读数据时的连接超时毫秒数RecvBufferSize属性:指明了用来接收数据的缓冲区字节大小,默认值为8192。SendBufferSize属性:指明了写数据到连接缓冲区时允许的最大字节数,默认值为32768LocalName属性:指明本地计算机名称。(2)TIdTCPClient组件的过程和方法介绍Connect形式:procedureConnect(constATimeout:integer);含义:用来向服务器请求建立一个连接ConnectAndGetAll形式:functionConnectAndGetAll():sting;含义:用于连接到Host属性和Port属性指定的远程计算机并从服务器中读取所有数据Connected形式:functionConnected():Boolean;含义:用来指明到对方计算机的连接是否已经建立OpenWriteBuffer形式:procedureOpenWriteBuffer(constAThreshhold:Integer);含义:打开写缓冲区CloseWriteBuffer形式:procedureCloseWriteBuffer();含义:关闭写缓冲区Disconnect形式:procedureDisconnect();含义:用于断开与对方计算机的连接FlushWriteBuffer形式:procedureFlushWriteBuffer(constAByteCount:Integer);含义:发送自调用OpenWriteBuffer以来的所有缓冲区数据到对方计算机,并随后清空缓冲区ReadBuffer形式:procedureReadBuffer(varABuffer;constAByteCount:Longint);含义:从Indy的接收缓冲区中读取的字节数ReadStream形式:procedureReadStream(AStream:TStream;AByteCount:LongInt;constAreadUntilDisconnect:Boolean);含义:从Indy缓冲中读取数据并存储在AStream参数指定的目的流中WriteBuffer形式:procedureWriteBuffer(constAbuffer;AByteCount:LongInt;constAwriteNow:Boolean)含义:用于向对方计算机发送数据,数据被写往Indy缓冲区或者直接发送给对方WriteStream形式:ProcedureWriteStream(AStream:Tstream;constAAll:Boolean;constAWriteByteCount:Boolean;constASize:Integer);含义:用于发送一个对象流(3)TIdTCPClient组件的事件介绍OnStatus事件:是一个TidSatusEvent类型的时间句柄,在当前连接状态改变时被触发2TIdTCPServer组件TIdTCPServer组件位于IndyServers组件板如图所示,TIdTCPServer组件封装了一格完整的多线程TCP服务器。TIdTCPServer使用一个或多个线程来监听客户连接。通过TidThreadMgr对象的关联,为每一个连接到服务器的客户链接分配一个独立的线程。TIdTCPServer允许配置服务器监听线程,包括默认端口、监听队列、最大连接数等。TIdTCPServer组件实现了两套机制来链接线程提供服务。第一种方法利用响应事件句柄的方法来处理客户链接。第二种方法使用TidCommandHandler对象来辨认合法的服务命令,提供一些方法和属性来处理参数,执行动作,表述正确或者错误的响应。(1)TIdTCPServer组件的属性介绍Active属性:用于指出TIdTCPServer的当前状态Bindings属性:为TCP服务器提供默认端口号,并被TidListenerThread对象用来获取对Socket句柄的访问和由TCP/IP协议栈提供的底层方法图11-5 IdTCPServer组件DefaultPort属性:指明了其监听新的客户端链接请求的端口号Greeting属性:当客户端链接请求被监听线程接收时,Greeting中包含了被发往客户端的欢迎信息ListenQueue属性:用来为监听线程指明可允许的、未处理的链接请求数目Threads属性:包含了在监听线程中创建的线程列表LocalName属性:标识了用户计算机系统的主机名(2)TIdTCPServer组件的过程和方法介绍BeginWork形式:ProcedureBeginWork(AWorkMode:TWorkMode;constSize:Integer);方法:用于触发OnBeginWork事件,同时维护读/写堵塞操作的数量,以及初始读写操作的大小信息DoWork形式:procedureDoWork(AWorkMode:TWorkMode;constACount:Integer);方法:用于触发OnWork事件。在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不产生任何效果。EndWork形式:procedureEndWork(AWorkMode:TWorkMode);方法:EndWork过程用于触发OnEndWork事件。EndWork上可以嵌套调用,但是onEndWork事件仅在第一次调用时触发(3)TIdTCPCServer组件的事件句柄介绍OnConnect事件:在客户线程试图连接到TCP服务器时触发OnDisconnect事件:在客户线程试图断开与TCP服务器时触发OnExecute事件:当客户线程试图执行TidPeerThread.Run方法时触发OnStatus事件:在当前连接的状态改变时被触发11.3.2使用UDP协议首先介绍TIdUDPClient、TIdUDPServer组件的用法1TIdUDPClient组件TIdUDPClient组件位于IndyClients组件板如图所示,TIdUDPClient组件用于实现基于UDP的客户方通信管理,用Send方法传输数据给由Host和Port属性指定的远程计算机。图11-8TIdUDPClient组件(1)TIdUDPClient的组件的属性介绍Host属性:用于标识远程计算机地址。Port属性:和Host属性一起指明远程计算机地址。ReceiveTimeout属性:指明接收包的超时毫秒数。Active属性:用于指明TIdUDPClient的Socket绑定是否已经分配。Binding属性:用于发送和接收数据的Socket绑定。BroadcastEnabled属性:用于指出Socket绑定是否能执行广播传输。BufferSize属性:用于表明传送的UDP数据包的最大字节数。默认数据包最大值是8192。LocalName属性:包含了本地计算机名。(2)TIdUDPClient的过程和方法Send形式:procedureSend(AHost:string;constAPort:Integer;constAData:string);含义:AData中的数据传送给Host属性和Port属性指定的远程计算机。SendBuffer形式:procedureSendBuffer(AHost:string;constPort:Integer;varABuffer;AByteCount:Integer);含义:用于传送数据给远程计算机。Boradcast形式:procedureBoradcast(constAData:string;constAPort:Integer);含义:向网络中的所有计算机广播AData中的数据。ReceiveBuffer形式:functionReceiveBuffer(varABuffer;constABufferSize:Integer;varVPeerIP:stringvarVPeerPort:integer;AMsec:Integer):integer;含义:用于从VPeerIP和VPeerPort参数指定的计算机中读出数据到ABuffer缓冲区。BeginWork形式:procedureBeginWork(AWorkMode:TWorkMode;constASize:Integer);含义:用于触发OnBeginWork事件,同时维护读写堵塞操作的数量,以及初始读写操作的大小信息。DoWork形式:procedureDoWork(AWorkMode:TWorkMode;constACount:Integer)含义:用于触发OnWork事件,在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不会产生任何效果。EndWork形式:procedureEndWork(AWorkMode:TWorkMode);含义:用于触发OnEndWork事件,EndWork可以嵌套调用,但是OnEndWork事件仅在第一次调用时触发。(3)TIdUDPClient的事件响应OnStatus事件:在当前链接的状态改变时被触发。2TIdUDPServer组件TIdUDPServer组件位于IdUDPServer组件板如图所示,TIdUDPServer组件用于实现基于UDP的服务器通信管理。下面介绍它的主要属性和方法:图11-9 TIdUDPServer组件(1)TIdUDPServer的属性Bindings属性:为TIdUDPServer提供默认端口号,并通过TIdUDPListenerTherad来访问Socket句柄和协议栈提供的底层方法。DefaultPort属性:用来标识由服务器创建的新的Socket绑定的端口号,新的链接用该端口号来进行监听。Active属性:用于指明TIdUDPServer的Socket绑定是否已经分配。Binding属性:用于发送和接收数据的Socket绑定。BroadcastEnabled属性:用于指明服务器是否正在向网络上的所有计算机广播数据报。BufferSize属性:用于用来指定能通过Binding发送和接收的最大UDP包,默认数据包最大值是8192。ReceiveTimeout属性:用来表明ReceiveString方法时等待的超时毫秒数。LocalName属性:标识用户计算机系统名。(2)TIdUDPServer组件的方法和过程Boradcast形式:procedureBoradcast(constAData:string;constAPort:Integer);含义:向网络中的所有计算机广播AData中的数据,Aport参数指明了计算机的端口号。ReceiveBuffer形式:functionReceiveBuffer(varABuffer;constABufferSize:Integer;varVPeerIP:stringvarVPeerPort:integer;AMsec:Integer):integer;含义:用于从VPeerIP和VPeerPort参数指定的计算机中读出数据到ABuffer缓冲中。Send形式:procedureSend(AHost:string;constAPort:Integer;constAData:string);含义:Send过程将AData中的数据传送给由AHost参数和APort参数指定的远程计算机。BeginWork形式:ProcedureBeginWork(AWorkMode:TWorkMode;constASize:Integer);含义:该过程可以被嵌套调用,但是OnBeginWork事件仅在第一次调用BeginWork方法时触发。DoWork形式:procedureDoWork(AWorkMode:TWorkMode;constACount:Integer);含义:用于触发OnWork事件。在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不产生任何效果。EndWork形式:procedureEndWork(AWorkMode:TWorkMode);含义:用于触发OnEndWork事件。EndWork可以嵌套调用,但是OnEndWork事件仅在第一次调用时触发。(3)TIdUDPServer的事件响应OnUDPRead事件:当数据已经从Socket中读出来可以被服务器使用时,由DoUDPRead方法触发。OnStatus事件:在当前链接的状态改变时被触发。第第12章章Delphi串口通信编程串口通信编程用Delphi实现串口通信,最常用的几种方法为:使用API函数、使用组件(如MSComm等)或者在Delphi中调用其他串口通信程序。12.1 RS-232C标准标准所谓串行通信接口标准,是指串行通信接口与外设的信号连接标准。实际中常用的串行通信接口标准有3种:RS-232C,RS-422A/423A和20mA电流环。常用的PC机都配置了RS-232C标准接口。RS-232C标准常简称为RS-232。RS-232C的定义包括电气特性(如电压值)、机械特性(如接头形状)及功能特性(如脚位信号)等。串行通信接口基本功能是:在发送时,把CPU送来的并行码转换成串行码,逐位地依次发送出去;在接收时,把发送过来的串行码逐位地接收,组装成并行码,并行地发送给CPU去处理。这种串行到并行转换的功能,常用硬件电路来实现,这种硬件电路叫做串行通信接口。普通的Modem通常都是通过RS-232C串行口信号线与计算机连接。根据RS-232C标准规定,接口电路采用一对物理D型连接器:DTE设备应该有一个D型插头接口,DCE设备应该有一个D型插座接口。D型连接可以是25芯(简称为DB25),也可以是9芯(简称为DB9)。RS-232C引脚分配如图12-1所示。图12-1 DB25与DB9引脚分配图12.1.1信号连接RS-232C规定使用一种DB25连接器,其中20个脚作了定义,9、10、11、18、25未作定义。RS-232C串行口信号分为3类:传送信号、联络信号和信号地。1传送信号(TxD和RxD)传送信号是经由(发送数据信号线,引脚)传送和(接收数据信号线,引脚)接收的信息格式即一个传送单位(字节)由起始位、数据位、奇偶校验和停止位组成。2联络信号(RTS、CTS、DTR、DSR、DCD和RI等个信号)RTS(请求传送,引脚),是PC向Modem发出的联络信号。高电压表不PC机请求向Modem传送数据。CTS(清除发送,引脚),是Modem向PC机发出的联络信号。高电压表示Modem响应PC发出的RTS信号,且准备向远端Modem发送数据。DTR(数据终端就绪,引脚),是PC向Modem发出的联络信号。高电压表示PC机处于就绪状态,本地Modem和远端Modem之间可以建立通信信道。若为低电平,则强迫Modem终止通信。DSR(数据装置就绪,引脚),是Modem向PC发出的联络信号。它指出本地Modem的工作状态,高电压表示Modem没有处于测试通话状态,可以和远端Modem建立通道。DCD(传送检测,引脚),是Modem向PC发出的状态信号,高电压表示本地DCE接收远端Modem发来的载波信号。RI(铃指示,引脚),Modem向PC发出的状态信号。高电压表示本地Modem收到远端Modem发来的振铃信号。3SG(信号地)SG(信号地,引脚)为相连的PC和Modem提供同一电势参考点。12.1.2握手DTE和DCE之间要实现双向通信,至少需要条信号线:TxD使数据从DTE到ECE。RxD使数据从ECE到ETE,SG为信号地。必须使用握手信号,它提供了一种控制数据流的方法,即接收设备可以控制发送设备的数据发送。在异步串行通信中,这称之为握手(handshaking)或流量控制(flowcontrol)。握手控制可以具体分为硬件握手(硬件流控)和软件握手(软件流控)。1硬件握手硬件握手是使用专门的握手电路去控制数据的传输。当接收设备准备好之后,就通过专用的握手电路传送一个正电压给发送设备,指示发送设备数据。如果接收传送一个负电压给发送设备,则指示发送设备停止发送数据。为了完成数据通信需要有类电路:数据线、信号线和握手线。(1)DTE到DCE为了控制DTE的发送数据,DCE使用DSR信号作为主握手信号去通知DTE已做好接收数据库的准备。当通知DTE暂停发送数据时,置DSR无效。(2)DCE到DTE为了控制DCE的数据发送,DTE使用DTR信号作为主握手信号去通知DCE已做好接收数据的准备。当通知DCE暂停发送数据时,置DTR无效。DTE还使用RTS信号作为第二握手信号控制DCE设备。仅当这两条握手线都有效时,DCE才发送数据。(3)双向通信双向通信中只使用主握手线,则共需要5条信号线:TxD、RxD、DSR、DTR和SG。如果还使用第二握手线,则共需要7条信号线。为了使DCE能向DTE提供更多信息,通常还使用RI和DCE两条信号线。这样一个完整的异步串行通信必需的就是这9条信号线。2软件握手软件握手的原理机制与硬件握手基本相同,不同的握手信号是在数据线(TxD和RxD)上进行传送的,而不是在专门握手线上传送。这是因为软件握手信号是由特殊字符组成的,所以传送这些字符必须使用数据电路,而不是使用专门握手电路。这种方法常用在直接连接或通过Modem连接的两台计算机之间进行双向通信的场合。RIDTRDCTSGDSRCTSRTSRxDTxD2345678202223456782022图12-2 有握手功能的双向通信软件握手最常用的协议是XON/XOFF协议。该协议主要解决通信双方处理速度不区配的问题,协议规定发送XOFF表示暂停发送数据,发送XON表示继续发送数据。3硬件与软件相结合的握手为了综合硬件握手和软件握手的好处,可以采用硬件和软件相结合的握手控制。假设DTE设备为计算机,DCE设备为Modem,两台计算机之间通过Modem经电话线连接,则此时计算机与Modem之间可采用硬件握手方法,而两台计算机之间可以使用软件握手方法进行联系。12.1.3微机的RS-232C接口个人计算机的RS-232C接口名称有多个:RS-232C口、串口、通信口、COM口、异步口等。目前DOS3.3以上版本和Windows3.2/98/NT最多支持个串口:COM1、COM2、COM3和COM4。它们所占用的I/O口地址和中断号见表:串口串口I/O地址地址中断号中断号COM10x3f8IRQ4COM0x2f8IRQ3COM0x3e8IRQ4COM0x2e8IRQ3为一更好地说明RS-232C接口电路的实际工作情况,下面以应答呼叫过程为例,具体分析其信号间的交互关系。所谓应答呼叫过程,即指Modem从接收到振铃信号开始,到数据传输结束后Modem和DTE恢复到原来的空闲状态为止的过程。(1)数据终端DTE的控制软件持续监视振铃指示(RI),等待该信号有效。引脚连线如图:CTS,清除发送DSR,DCE准备就绪SG,信号地DCT,载波检测DTR,DTE准备就绪RI,振铃指示RTS,请求反送RxD,接收数据TxD,发送数据2345678202223456782022图12-3 DTE和Modem的引脚边线(2)响铃后,Modem在振铃脉冲期间发出振铃指示信号(RI有效),在振铃脉冲间隔期间,振铃指示信号有效。即随着振铃脉冲的有无,RI信号ON/OFF交替变化。(3)DTE的通信控制软件在检测到振铃指示后,开始通过计算机振铃指示ON/OFF变化的次数对振铃进行计数。当达到程序预置好的振铃数时,控制软件发出数据终端就绪信号(DTR有效),迫使Modem进入摘机状态,开始应答电话。(4)Modem在等待一小段时间后,自动地发送它的应答载波信号。同时Modem发出数据设备就绪信号(DSR信号有效),通知DTE已完成所有准备工作,正在等待对方载波信号。(5)在DTE发出数据终端就绪信号(DTR有效)期间,DTE的控制软件监视数据设备就绪信号(DSR是否有效)。当DSR变为ON状态后,DTE便知道了Modem已准备建立数据链路,于是DTE开始监视载波检测(DCD)信号,以检查数据链路是否已建立。(6)当主叫Modem的载波信号出现在电话线上时,被叫Modem就发出载波检测信号(DCD),通知DTE已建立数据链路。(7)在数据链路连接期间,发送数据(TxD)和接收数据(RxD)线上即开始了全双工通信。同时,DTE仍监视着载波检测(DCD)信号,以确定数据链路是否连接。(8)数据传输结束后,DTE使数据终端就绪信号(DTE无效),Modem撤消载波信号并以载波检测(DCD)和数据设备就绪(DSR)信号无效给予响应。数据链路释放后,Modem和DTE准备下一次接收或作另一次呼叫。12.2串行口串行口API函数函数12.2.1常用的串行通信操作函数1CreateFileCreateFile创建或打开一下的对象并返回句柄完整定义:Handle CreateFile(LPCTSTRlpFileName,/文件名DWORDdwDesireAccess,/访问模式(读/写)DWORDdwShareMode,/共享模式LPSECURITY_ATTRIBUTESlpSecurityAttributes,/安全属性DWORDdwCreationDistribution,/文件已经存在或不存在时的处理方法DWORDdwFlagsAndAttributes,/文件属性,对于串口来说有意义的属性只有FILE_FLAG_OVERLAPPED,表示端口的I/O可以在后台进行(后台IO也叫异步IO)HANDLEhDemplateFile/复制制定文件的扩展属性);2CloseHandleCloseHandle函数关闭一个已打开的对象句柄,完整定义:BOOL CloseHandle(HANDLEhObject/句柄);3SetupCommSetupComm为通信设备初始化参数(设置通信缓冲区的大小),完整定义;BOOL SetupComm(HANDLEhFile,/句柄DWORDdwInQueue,/输入缓冲区的大小DWORDdwOutQueue/输出缓冲区的大小);4ReadFileReadFile同步或异步从文件读取数据,在读之前可能要调整文件指针的位置,完整定义:BOOL ReadFile(HANDLEhFile,/句柄LPVOIDlpBuffer,/接收数据的缓冲区地址DWORDnNumberOfBytesToRead,/读取的字节数LPDWORDlpNumberOfBytesRead,/读取字节数的地址LPOVERLAPPEDlpOverlapped/当打开文件制定dwFlagsAndAttributes参数为FILE_FLAG_OVERLAPPED时,这个参数就必须应用一个特殊的结构,结构中定义一次异步读操作。否则,该参数应置为空。);5WriteFileWriteFile同步或异步写数据到文件中,在写之前可能要调整文件指针的位置,完整定义:BOOL WriteFile(HANDLEhFile,/句柄LPCVIODlpBuffer,/指向缓冲区的数据DWORDnNumberOfBytesToWrite,/要写的字节数LPDWORDlpNumberOfBuffersWritten,/返回实际写的字节数LPOVERLAPPEDlpOverlapped/当打开文件指定dwFlagsAndAttributes参数为FILE_FLAG_OVERLAPPED时,这个参数就必须引用一个特殊的结构,结构中定义一次异步写操作。否则,该参数应置为空。);6SetCommStateSetCommState用制定的DCB结构设置通信参数,将重新初始化硬件和控制设置,但不会清空输入输出缓冲区。DCB结构中包含波特率、数据位、校验位、停止位和流控制方式等信息。完整定义:BOOL SetCommState(HANDLEhFile,/句柄LPDCBlpDCB/指向硬件控制块);7GetCommStateGetCommState返回当前通信参数的DCB结构。DCB结构中包含波特率、数据位、校验位、停止位和流控制方式等信息,完整定义:BOOL GetCommState(HANDLEhFile,/句柄LPDCBlpDCB/指向硬件控制块);8ClearCommErrorClearCommError清除串口错误并获取当前状态(可以返回接收缓冲区中处于等待状态的字节数)。完整定义:BOOL ClearCommError(HANDLEhFile,/句柄LPDWORDlpErrors,/接收错误代码LPCOMSTATlpStat/指向通信设备的状态缓冲区);9BuildCommDCBBuildCommDCB函数用制定的设备控制串填充DCB结构,设备控制串可用相应的模式控制命令得到。要使设置生效,还需调用SetCommState。完整定义:BOOL BuildCommDCB(LPCTSTRlpDef,/指向设备控制串LPDCBlpDCB/指向设备控制块);10BuildCommDCBAndTimeoutsBuildCommDCBAndTimeouts函数用指定的设备控制串填充DCB结构,并设置超时值、未超时值。设备控制串可用相应的模式控制命令得到。这个函数综合了BuildCommDCB和SetCommTimeouts两个函数,完整定义:BOOL BuildCommDCBAndTimeouts(LPCTSTRlpDef,/设备控制串LPDCBlpDCB,/设备控制块LPCOMMTIMEOUTSlpCommTimeouts/超时结构);11ClearCommBreakClearCommBreak函数恢复发送缓冲区中的数据传送,并把线路置为nonbreak状态(可参阅SetCommBreak和TransmitCommChar)。完整定义:BOOL ClearCommBreak(HANDLEhFile/句柄);12CommConfigDialogCommConfigDialog函数显示配置端口的对话框,完整定义:BOOL CommConfigDialog(LPTSTRlpszName,/设备名字字符串HWNDhWnd,/窗口句柄LPCOMMCONFIGlpCC/Comm配置结构);13DeviceIoControlDeviceIoControl函数直接发送控制指令到指定的设备,让设备执行特定的操作。完整定义:BOOL DeviceIoControl(HANDLEhDevice,/句柄DWORDdwIoControlCode,/控制指令LPVOIDlpInBuffer,/指定指令所需的数据缓冲区DWORDnInBufferSize,/lpInBuffer缓冲区的大小LPVOIDlpOutBuffer,/指定指令返回的数据缓冲区DWORDnOutBufferSize,/lpOutBuffer缓冲区的大小LPDWORDlpBytesReturned,/lpOutBuffer缓冲区返回数据的实际大小LPOVERLAPPEDlpOverlapped/指向Overlapped结构);14EscapeCommFunctionEscapeCommFunction函数直接让设备执行指定的扩展操作,用于完全控制端口。完整定义:BOOL EscapeCommFunction(HANDLEhFile,/句柄DWORDdwFunc/要执行的扩展功能);15GetCommConfigGetCommConfig函数获得当前设备的设置,王政定义:Bool GetCommConfig(HANDLEhCommDev,/句柄LPCOMMCONFIGlpCC,/Comm配置结构地址LPDWORDlpdwSize/缓冲区大小);16GetCommMaskGetCommMask函数返回指定的设备的事件掩码,完整定义:BOOL GetCommMask(HANDLEhFile,/句柄LPDWORDlpEvtMask/返回的事件掩码);17GetCommModemStatusGetCommModemStatus函数返回Modem的控制寄存器的值,完整定义:BOOL GetCommModemStatus(HANDLEhFile,/句柄LPWORDlpModemStat/控制寄存器的值);18GetCommPropertiesGetCommProperties函数返回指定设备的属性。在调用SetCommState之前常用此函数判断是否支持指定的设置值,例如,是否支持的波特率等。完整定义:BOOL GetCommProperties(HANDLEhFile,/句柄LPCOMMPROPlpCommProp/属性结构);19GetCommStateGetCommState函数返回指定设备当前设置的设备控制块,完整定义:BOOL GetCommState(HANDLEhFile,/句柄LPDCBlpDCB/设备控制块);20GetCommTimeoutsGetCommTimeouts函数返回指定设备的所有读写操作超时值,完整定义:BOOL GetCommTimeouts(HANDLEhFile,/句柄LPCOMMTIMEOUTSlpCommTimeouts/超时结构);21GetDefaultCommConfigGetDefaultCommConfig函数返回通信设备的默认值配置,完整定义:BOOL GetDefaultCommConfig(LPCSTRlpszName,/设备名字符串LPCOMMCONFIGlpCC,/配置结构LPDWORDlpdwSize/结构的大小);22PurgeCommPurgeComm函数取消输入或输出缓冲区的所有字符,并中止悬而未决的读或写操作,完整定义:BOOL PurgeComm(HANDLEhFile,/句柄DWORDdwFlags/取消操作的参数);23SetCommBreakSetCommBreak函数暂停发送缓冲区的数据传送,并把线路为break状态,直到调用ClearCommBreak时才恢复。完整定义:BOOL SetCommBreak(HANDLEhFile/句柄);24SetCommConfigSetCommConfig函数设置通信设备的当前配置,完整定义:BOOL SetCommConfig(HANDLEhCommDev,/句柄LPCOMMCONFIGlpCC,/配置结构DWORDdwSize/结构的大小);25SetCommMaskSetCommMask函数设置指定设备的事件掩码。调用此函数后,需要再调用WaitCommEvent来等待事件的产生。完整定义:BOOL SetCommMask(HANDLEhFile,/句柄DWORDdwEvtMask/事件掩码);26SetCommTimeoutsSetCommTimeouts函数设置读和写操作的超时值,完整定义:BOOL SetCommTimeouts(HANDLEhFile,/通信设备句柄LPCOMMTIMEOUTSlpCommTimeouts/超时结构);27SetDefaultCommConfigSetDefaultCommConfig函数设置通信设备的默认配置,完整定义:BOOL SetDefaultCommConfig(LPCSTRlpszName,/设备名字符串LPCOMMCONFIGlpCC,/配置结构DWORDdwSize/结构的大小);28TransmitCommCharTransmitCommChar函数向指定设备发送字符,该字符将优先于输出缓冲区中的数据。一般情况下,先调用SetCommBreak,再调用此函数,最后调用ClearCommChar,用于优先反送指定字符。完整定义:BOOL TransmitCommChar(HANDLEhFile,/句柄CharcChar/发送的字符);29WaitCommEventWaitCommEvent函数等待指定设备的事件发生。一系列的事件被此函数监视,包括设备相关的事件掩码,可以同步或异步方式进行。完整定义:BOOL WaitCommEvent(HANDLEhFile,/句柄LPDWORDlpEvtMask,/要处理的事件LPOVERLAPPEDlpOverlapped,/Overlapped结构,用于异步方式);12.3 MSComm控件控件12.3.1MSCom安装MSCcomm组件是MicrosoftVisualStudio配带的ActiveX组件,一般安装MicrosoftVisualStudio后这些文件会自动生成,然后在Delphi中安装MSComm控件。步骤如下:(1)先打开Delphi7.0集成开发环境,选择菜单“Component”中的“ImportActiveXControl”命令,在“ImportAcitiveX”选项卡内选择“MicrosoftCommControl6.0”项。(2)单击“Install”按钮安装MSComm控件,安装后在“ActiveX”组件板中出现MSComm图标,即可被使用。MSCOMM32.OCX可以按如下两种方式注册:第一种方式:点击【开始】|【运行】,在运行命令栏中填入如下命令:Regsvr32c:windowssystemmscomm32.ocx第二种方式:打开记事本输入以下内容,并且保存未REG的扩展名,双击此文件也可以进行注册,REGEDIT4HKEY_CLASSES_ROOTLicenses4250E830-6AC2-11cf-8ADB-00AA00C00905=”kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun”12.3.2MSComm控件方法MSComm控件提供下列两种处理通信的方式:(1)事件驱动通信是处理串行端口交互作用的一种非常有效的方法。在许多情况下,在事件发生时需要得到通知。(2)在程序的每个关键功能之后,可以通过检查CommEvent属性的值来查询事件和错误。如果应用程序较少,并且是自保持的,这种方法可能是更可取的。每个MSComm控件对应着一个串行端口。如果应用程序需要访问多个串行端口,必须使用多个MSComm控件。可以在Windows“控制面板”中改变端口地址和中断地址。尽管MSComm控件有很多重要的属性,但首先必须熟悉几个属性。CommPort设置并返回通信端口号。Settings以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位。PortOpen设置并返回通信端口的状态,也可以打开和关闭端口。Input从接收缓冲区返回和删除字符。Output向传输缓冲区写一个字符串。12.3.3MSComm控件属性通信MSComm控件提供了27个关于通信控制方面的属性和5个标准属性。1Break属性描述:设置或清除中断信号的状态。该属性在设计时无效。语法:form . MSComm.Break:=True|False设置为:True设置中断信号状态False清除中断信号状态2CDHolding属性通过查询CarrierDetect(CD)信号线的状态确定当前是否有传输。语法:form. MSComm.CDHolding:=True|FalseCDHolding属性的设置值为:TrueCarrierDetect信号线为高电平FalseCarrierDetect信号线为低电平3CommID属性返回一个说明通信设备的句柄。该属性在设计时无效,在运行时为只读。语法:form . MSComm.CommID4CommEvent属性返回最近的通信事件或错误。该属性在设计时无效,在运行时为只读。语法:form . MSComm.CommEvent5CommPort属性设置并返回通信端口号。语法:form . MSComm.CommPort:=value6CTSHolding属性确定是否可通过查询ClearToSend(CTS)信号线的状态发送数据。语法: form . MSComm.CTSHolding:=True|FalseCTSHolding属性的设置值为:TrueClearToSend信号线为高电平FalseClearToSend信号线为低电平7DSRHolding属性确定DataSetReady(DSR)信号线的状态。语法:form . MSComm.CSRHolding:=True|FalseCSRHolding属性返回以下值:TrueDataSetReady信号线为高电平FalseDataSetReady信号线为低电平8DTREnable属性确定在通信时是否使DataTerminalReady(DTR)信号线有效。语法:form . MSComm.DTREnable:=True|FalseDTREnable属性设置值:TrueDataTerminalReady信号线有效FalseDataTerminalReady信号线无效(缺省)9EOFEnable属性EOFEnable属性确定在输入过程中MSComm控件是否寻找文件结尾(EOF)字符。语法:form . MSComm.EOFEnable:=True|Falsevalue的设置值:True当EOF字符找到时OnComm事件被激活。False当EOF字符找到时OnComm事件不被激活(缺省)。10Handshaking属性设置并返回硬件握手协议。语法:form . MSComm.Handshaking:=value11InBufferCount属性返回接收缓冲区中等待的字符数。该属性在设计时无效。语法:form . MSComm. InBufferCount:=value12InBufferSize属性设置并返回接收缓冲区的字节数。语法:form . MSComm. InBufferSize:=value13Input属性返回并删除接收缓冲区中的数据流。该属性在设计时无效,在运行时为只读。语法:form. MSComm.Input14InputLen属性设置并返回Input属性确定被Input属性读取的字符数。语法:form.MSComm.InputLen:=value15InputMode属性设置或返回Input属性取回的数据的类型。语法:form.MSComm.InputMode:=value16NullDiscard属性确定NULL字符是否从端口传送接收缓冲区。语法:form.MSComm.NullDiscard:=valuevalue设置值是:TureNULL字符不从端口传送到接收缓冲区FalseNULL字符从端口传送到接收缓冲区(缺省值)17OutBufferCount属性返回在传输缓冲区中等待的字符数,也可以用它来清除传输缓冲区。该属性在设计时无效。语法:form.MSComm.OutBufferCount:=value18OutBufferSize属性以字节的形式设置并返回传输缓冲区的大小。语法:form.MSComm.OutBufferSize:=value19Output属性往传输缓冲区写数据流。该属性在设计时无效,在运行时为只读。语法:form.MSComm.Output:=value20ParityReplace属性当发生奇偶校验错误时,设置并返回换数据流中一个非法字符的字符。语法:form.MSComm.ParityReplace:=value21PortOpen属性设置并返返回通信端口的状态(开或关)。该属性在设计时无效,在运行时该才可用。语法:form.MSComm.PortOpen:=valuevalue设置值是:True端口开False端口关22Rthreshold属性在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm之前,设置并返回要接收的字符数。语法:form.MSComm.Rthreshold:=value23RTSEnable属性确定是否使RequestToSend(RTS)信号线有效。语法:form.MSComm.RTSEnable:=valuevalue设置值:TrueRequestToSend信号线有效FalseRequestToSend信号线无效(缺省)24Settings属性设置并返回波特率、奇偶校验、数据位和停止位参数。form.MSComm.Settings:=value25MSComm控件设置CommEvent属性为comEvSend并产生OnComm事件之前,设置并返回传输缓冲区中允许的最小字符数。语法:form.MSComm.SThreshold:=value12.3.4MSComm控件事件的介绍OnComm事件无论何时当CommEvent属性的值变化时,就产生OnComm事件,标志发生了一个通信事件或一个错误。语法:Procedure MSCommComm(Sender:Tobject);12.4 MSComm控件的错误消息控件的错误消息MSComm控件的错误(Error)常数见表:常数常数值值含义描述含义描述ComEventBreak1001接收到中断信号ComEventCTSTO1002Clear-to-send超时ComEventDSRTO1003Data-setready超时ComEventFrame1004帧错误ComEventOverrun 1006端口超速ComEventCDTO1007CarrierDetect超时ComEventRxOver1008接收缓况区溢出ComEventRxParity 1009错误ComEventTxFull1010传输缓冲区满ComEventDCB1011检索端口的设备控制块时出现的意外错误第第13章章 多线程程序设计多线程程序设计13.1线程的基本概念线程的基本概念线程是应用程序中的一条基本的执行路径,它也是win32进程中的最小执行单元,线程由一个堆栈、cpu寄存器的状态和系统调度列表中的一个入口组成,每个线程都可以访问进程中的所有资源。一个进程由一个或多个线程、代码、数据和应用程序在内存中的其他资源组成。低优先级的线程一般要等待高优先级线程。一般每个线程相互独立运行,各线程间应共享资源,然而必须通过信号或其他进程内通信的方法来协调线程之间的工作。使用线程可以在下面几个方面增强用户应用程序的性能:1避免瓶颈2并行操作3多处理器13.1.1线程的优先级每个线程的优先级由下面的标准决定:(1)其他进程的优先级类(高、普通或空闲)。(2)其他进程优先级类中线程的优先级(最低、普通下、普通、普通上、最高)。(3)动态优先级增高,如果有的话,系统将在线程的基础优先级上增加。在创建线程时,用户并没有用数字为它们指定优先级,系统将用两个步骤来确定线程的优先级,第一步是给进程分配一个优先级类,进程的优先级类将告诉系统进程与系统中的其他进程相对的优先级。第二步是为该进程所拥有的线程分配相对优先级。13.1.2线程的同步为了避免线程之间的冲突,有必要对访问共享资源的线程进行同步控制设计,同步还可以使线程之间相互依赖的代码能够正确运行。Win32的API提供了如下一组可以使其句柄用作同步的对象。(1)同步对象:互斥对象(Mutext)、信号灯和事件(Event)句柄(2)文件句柄(3)命令管道句柄(4)控制台输入缓冲区句柄(5)通信设备句柄(6)进程句柄(7)线程句柄13.1.3线程的局部存储(TLS)线程的局部变量对运行此函数的各个线程是局部的,但是当线程调用另一个函数时,该函数使用的静态或全局变量对所有线程来说将是同样的值。使用线程局部存储方法,可通过对进程中用于存储和获取各个线程不同值的索引来完成对一个线程的存储分配。13.2 定义线程对象定义线程对象13.2.1创建线程对象要创建一个新的Tthread派生类,可以使用如下步骤:(1)通过Delphi主菜单的【File】|【New】|【Other】在弹出的【NewItems】对话框中,选择TthreadObjec图标,单击【OK】按钮,系统将自动创建一个TthreadObject。(2)系统弹出NewsTthreadObject对话框,在其中输入一个新的类名和线程名。输入类名和线程名之后,Delphi将为用户创建一个用于实现线程的新单元文件。13.2.1初始化线程对象1为线程指定一个优先级但是也不能无休止的提高大量占用CPU的线程的优先级,否则可能会导致其他线程不能运行。应该只为那些花费大量时间等待一个外部事件的线程指定高优先级。2指定是否释放线程最简单的方法是让线程自己释放。这种情况下,可以将FreeOnTerminate属性值设为True。然而,有时用户线程对象可能会代表一个应用程序要反复执行的一个任务。13.2.3编写线程函数使用主VCL线程当用户使用VCL对象库中的对象时,他们的属性和方法不能保证线程是安全的,也就是说,访问属性或执行方法可能会执行一些使用了未受保护的内存的操作。如果所有对象在一个独立线程中访问它们的属性和执行方法,用户就不必担心对象之间彼此干扰,这时要使用主VCL线程,创建一个执行必要操作的独立过程,然后在用户编程的Synchronize方法中调用这个过程。在下述几种情况下,用户不需要使用Synchronize方法:(1)数据访问组件是线程安全的。(2)图形对象是线程安全的。(3)当使用一个线程安全的TthreadList版本时。1、使用线程局部变量线程函数及其调用的任何过程都有自己的局部变量。这些过程也可以访问全局变量。有时用户可能要使用一些特殊变量,他们对用户线程中所有过程而言是全局的变量,但却不能被叫一个线程类的其他实例共享。2检查是否被其他线程终止用户线程对象在Execute方法调用时开始运行,并且在Execute方法结束时终止。然而有时应用程序需要一个线程持续执行,直到某个外部条件得到满足。这时,用户可以让其他的线程通过Terminated属性来通知用户线程终止。当其他线程想终止用户线程时,可以调用Terminate方法,该方法将用户线程对象的Terminated属性值设为True。13.2.4编写线程的清除代码OnTerminate事件处理过程不作为用户线程的一部分运行,它在主VCL线程中运行,因此必须注意以下两点:(1)用户在OnTerminate事件处理过程中不能使用任何线程局部变量;(2)用户在OnTerminate事件处理过程中可以安全的访问任何组件以及VCL对象而不必担心与其他线程发生冲突。13.3使用线程对象13.3.1线程的同步VCL支持三种方法来避免其他线程与用户线程访问同样的内存区域。1锁住对象2使用临界区临界区就像一个门,一次只允许一个线程进入。要使用临界区,就要创建一个全局TcriticalSection对象。该对象有两个方法:Acquire(阻塞其他线程执行临界区的代码)和Release(释放阻塞)。每个临界区都和用户要保存的全局内存相联系,每个线程在访问一个全局内存之前都应该首先调用Acquire方法以确保没有其他线程在使用它。3使用multireadexclusivewrite同步当用户使用临界区保护全局内存时,每次只有一个线程可以使用该内存区,这样的保护可能满足不了用户的要求,特别是用户要求有一个必须经常读而很少写的对象或变量的时候。13.3.2执行线程对象1重载优先级当在线程中指定它所能得到的CPU时间时,应在构造函数中指定线程的优先级。然而,如果线程的优先级依赖于线程何时执行,就应该创建可以进入挂起状态的线程,设置线程的优先级,然后开始执行程序。2启动和停止线程一个线程在运行前可以被启动和中止很多次。要临时中止一个线程的执行,可以使用线程的Suspend方法。用户可以调用Terminate方法要求一个线程提前停止,该方法将线程的Terminated属性设置为True。3暂存线程要暂存线程,用户必须维护一个已经创建的线程的列表,这个列表可以由使用线程的一个对象维护;另一个办法是用户可以使用一个全局变量来暂存线程。13.4 利用多线程排序利用多线程排序1界面设计向窗体中增加3个Label、3个PaintBox和1个Button控件。2程序设计第第14章章 面向对象程序设计面向对象程序设计面向过程的程序设计着眼于系统实现的功能,采用自顶向下,逐步细化的方法进行功能分解直至建立系统的功能结构和相应的程序模块。类(Class)是具有相同属性和操作的对象的集合。类是进行数据抽象的基本单位。每一个对象都是类的一个实例(Instance)。所谓继承是父类(BaseClass)(基类)可以派生自己的子类(DerivedClass)(派生类),子类除了继承父类的属性和操作之外,还具有自己独特的属性和操作。通信是实现各个不同对象之间消息传递的方法。所谓消息实际上是一个类的对象要求另一个类的对象执行操作的指令。14.1对象的基本概念14.1.1对象的特性一个对象,其最突出的特征有三个:封装性、继承性、多态性。1对象的封装性对象的封装特性是把数据和代码结合在同一个结构中。将对象的数据域封闭在对象的内部,使得外部程序必须而且只能使用正确的方法才能对要读写的数据域进行访问。2对象的继承性对象的继承性是指把一个新的对象定义成为已存在对象的后代。新对象继承了旧类的一切东西。3对象的多态性多态性是在对象体系中把设想和实现分开的手段。多态的含义是指某一个标识符表示多种类型的变量,或者标识不同意义的函数或过程。14.1.2从一个对象中继承数据和方法在窗体上单击鼠标或用ObjectInspector的上端的ObjectSelector选中Form1对象,按键查阅他的在线帮助,会在Properties和Metehod中找到它的继承到的全部属性和方法。当在工程中加入一个新窗体时,就等于加入了一个基本模型。通过不断地在窗体中加入部件,就自行定义了一个新的窗体。要自定义任何对象,都将从已经存在的对象中继承域和方法,建立一个该种对象的子类。14.1.3对象的范围一个对象的范围决定了它的数据域、属性值、方法的活动范围和访问范围。在一个对象的声明部分声明的数据域、属性值、方法都只是在这个对象的范围中,而且只有这个对象和它的后代才能又拥有它们。虽然这些方法的实际程序代码可能是在这个对象之外的程序库单元中,但这些方法仍然在这个对象的范围内,因为它们是在这个对象的声明部分中的声明的。当在一个对象的事件处理过程中编写程序代码来访问这个对象的属性值、方法或域时,不需要在这些标识符之前加上这个对象变量的名称。14.1.4对象共有域和私有域的声明可以在对象的Public或Private部分加入新的数据域和方法。Public和Private是ObjectPascal的保留字。在Pbulic部分中声明其他库单元中对象的方法也可以访问的数据域或方法。在Private部分的声明有访问的限制。如果在Private中声明域和方法,那么它在声明这个对象的库单元外是不透明的,而且不能被访问。Private中可以声明只能被本可单元方法访问的数据域和本库单元对象访问的方法。14.1.5访问对象的域和方法当想要改变一个窗体对象的一个域的某个属性,或是调用它的一个方法是,必须在这个属性名称或调用方法之前加上这个对象的名称。同样想改变一个窗体对象中一个对象域的多个属性或调用多个方法时,使用with语句可以简化程序。With语句在对象中可和在记录汇总一样使用。14.1.6对象变量的赋值如果两个变量类型相同或兼容,可以把其中一个对象变量赋给另一个对象变量。只要赋值的对象变量是被赋值的对象变量的祖先类型,就可以将一个对象变量赋给另一个对象变量。14.1.7建立非可视化对象1声明一个非可视化对象可以用如下的方法建立一个自己的TWorker非可视化对象TypeTWorker=Class(TObject)Title:=String20;Name:=String20;HourlyPayRate:real;FunctionCalculatePayAmount:real;end;2用Create方法建立对象实例TWorker只是一个对象类型除非通过一个构造函数的调用从而被实例取代或创建,否则一个对象并不存储在内存中。构造函数是一个方法,它为新对象配置内存并且指向这个新的对象。这个新的对象也被称为这个对象类型的一个实例。建立一个对象的实例,需要调用Create方法,然后构造函数把这个实例赋给一个变量。如果想声明一个TWorker类型的实例,在访问这个对象的任何域之前,的程序代码必须调用Create。Worker:= Tworker.Create;3撤销对象当使用完对象后,应该及时撤销它,以便把这个对象占用的内存释放出来。可以通过调用一个注销方法来撤销的对象,它会释放分配给这个对象的内存。Delphi的注销方法有两个:Destroy和Free。Delphi建议使用Free,因为它比Destroy更为安全,同时调用Free会生成效率更高的代码。可以用下列的语句释放用完的Worker对象:Worker.Free;14.2 类类型和对象类类型和对象对象是类的实例(instance),即由类定义的数据类型的变量。对象是实体,当程序运行时,对象为它们的内部表达占用一些内存。对象与类的关系就像变量与类型的关系。在ObjectPascal中,声明类数据类型使用保留字Class。类类型声明的一般格式为:Type=Class()End;有关类类型的儿点声明:(1)类名可以是任何合法的标识符,在Delphi中,类类型的标识符一般以T打头。(2)Class是保留字,表示声明类型是类类型。(3)Class后面的父类名表示当前声明的类是从父类名制定的类中派生出来的,声明的类称为父类的子类或直接后代,该子类将继承父类及所有祖先的所有成员。(4)“父类名”是可以省略的。(5)类类型声明中可以没有成员列表,如果需要,类类型可以有3类成员,分别是Field(字段)、Method(方法)、property(特性)。(6)在类的声明中如果含有字段成员,那么字段成员的声明必需优先于特性和方法成员的声明。(7)跟其他数据类型不同的是,类类型的声明只能出现在Program单元或UNIT单元最外层作用域的类型定义部分,而不能定义在变量声明部分或一个过程或函数内。因此,类类型的作用域总是全局的。(8)一旦声明了类类型,其使用同其他数据类型一样,可以创建这个类的多个实例(对象),所有创建的对象将共享该类的成员。14.3类的方法14.3.1方法的声明声明一个方法的格式同卢明一个过程或函数的语法相似,过程方法的声明格式如下:Procedure (方法名方法名)();函数方法声明的一般格式为:Function (方法名方法名)():;其中,方法名可以足任何合法的标识符,参数表是可选的,如果没有参数可省略括号。方法可分为4种类型,分别是构造、析构、过程和函数,它们分别用Constuctor、Destructor、Procedure、Function这4个符号来声明。在定义方法时,可以直接使用类中已声明的字段,不需要作为参数来传递,访问这些字段时也不需要引用限定符。14.3.2构造和析构1构造构造的声明同过程方法或函数方法类似,只是保留字不同。其声明格式为:Constructor();其中,构造名可以是任何合法的标识符,不过按照Delphi的习惯,构造名常使用Create。构造用于建立对象,并对对象进行初始化。通常,当调用构造时,构造类似一个函数,返回一个新分配的并初始化了的类类型实例。构造跟一般方法不同的是,一般方法只能在对象实例中引用,而构造既可以由一个对象实例引用,也可以直接由类来引用。2析构析构的声明格式为:Destructor();其中,析构名可以是任何合法的标识符,按照习惯,析构名常使用Destroy。析构的作用跟构造正相反,它用于删除对象并指定删除对象时的动作,通常是释放对象所占用的堆和先前占用的其他资源。构造的定义中,第一句通常是调用祖先类的构造,而析构正相反,通常是最后一句调用祖先类的析构。14.3.3方法指令字一个类中的方法可以通过在声明中使用指令字指定成静态、动态、虚拟和消息方法。方法按指令字分可分为三种,分别是虚拟、动态、消息方法,它们分别是方法名后用Virtual,Dynamic,Message保留字指定。也可以不加方法指令字,这种情况下声明的方法是静态的(static)。一个方法也可以像函数那样,指定参数的传递的方式,也即方法的调用约定。1静态方法静态方法类似于通常的过程和函数,编译器在编译时就已指定了输出该方法的对象实例。静态方法的主要优点是调用的速度快。当从一个类派生一个类时,静态方法不会改变。如果你定义一个包含静态方法的类,然后派生一个新类,则被派生的类在同一地址共享基类的静态方法,也就是你不能重载静态方法。如果你在派生类定义一个与祖先类相同名的静态方法,派生类的静态方法只是替换祖先类的静态方法。2虚拟方法虚拟方法比静态方法更灵活、更复杂。虚拟方法的地址不是在编译时确定的,而是程序在运行期根据调刚这个虚拟方法的对象实例来决定的,这种方法又为滞后联编。虚拟方法在对象虚拟方法表(VMT表)中占有一个索引号。VMT表保存类类型的所有虚拟方法的地址。当你从一个类派生一个新类时,派生类创建它自己的VMT,该VMT包括了祖先类的VMT,同时加上自己定义的虚拟方法的地址。虚拟方法可以在派生类中重新被定义,但祖先类中仍然可以被调用。3动态方法当把一个基类中的某个方法声明为动态方法时,派生类可以重载它。,被声明为动态的方法不是放在类的虚拟方法表中,而是由编译器给它一个索引号(一般不直接用到这个索引),当调用动态方法时,由索引号决定调用方法的哪个来具体实现。虚拟方法和动态方法几乎完全相同,只不过虚拟方法在调用速度上较快,但类型对象,占用空间大,而动态方法在凋用速度上稍慢而对象占用空间小。如果一个方法经常需要调用,或该方法的执行时间要求短,则在虚拟和动态之间还是选择使用虚拟为好。4消息句柄方法在方法定义时加上一个message指令字,就可以定义一个消息句柄方法。消息句柄方法主要用于响应并处理某个特定的事件。14.3.4抽象方法所谓抽象方法,首先必须是虚拟的或动态的,其次它只有声明而没有定义,只能在派生类中定义它(重载)。因此定义一个抽象方法,只是定义它的接口,而不定义底层的操作。它只是正当前类类型的声明中进行声明,但一般不在当前类中实现,它的实现会后置到子类进行。抽象方法声明的般格式为:Procedure ();Virtual/dyname;abstract;Function 方法名方法名)();Virtual/dyname;abstract;14.3.5重载方法与重定义方法1重载方法在子类中重载一个滞后联编的对象方法,需要使用保留字override。只有在祖先类中定义对象方法为虚拟后,才能进行重载。否则,对于静态对象方法,没有办法激活滞后联编,只有改变祖先类的代码。为重新定义静态对象方法,用户只需向子类添加该对象方法,它的参数可以与原来方法的参数相同或不同,而不需要其他特殊的标志。重载虚拟方法,必须指定相同的参数并使用保留字override。重载对象方法有两种典型的方法。一种是用新版本替代祖先类的方法,另一种是向现有方法添加代码。2重定义方法对象可以有多个同名的方法,这些方法被称为重新定义的方法(Overload)。14.4类的特性14.4.1声明特性要声明特性,必须声明三件事情:特性名、特性的数据类型、读写特性值的方法。ObjectPascal使用保留字Property声明特性,其声明的一般格式为:Property :read/ Write/;特性的声明由保留字Property、特性名标识符,可选的特性接口(PropertyInterface)和特性限定符(PropertySpecifier)构成。特性接口指定特性的数据类型,参数和索引号。14.4.2特性限定符特性限定符可以有4类,分别是Read,write,Stored和Default。其中Read和Write限定符用于指定访问特性的方法或字段。1Read限定符Read限定符的语法格式为:Read /Read限定符用于指定读取特性的方法或字段,通常是一个不带参数的函数,返回的类型就是特性的类型,并且函数名通常以“Get”加特性名组成,如一个读取Caption特性的方法通常命名为GetCaption。2Wrire限定符Write限定符的语法格式为:Write /Write限定符用于指定修改特性的方法,通常是一个与特性同类型的过程,这个参数用于传递特性新的值,并且过程名通常以“Set”加特性名组成。在Write限定符指定的方法的定义中,通常首先是把传递过来的值跟原先的值比较,如果两者不同,就把传递过来的特性值保存在一个字段中,然后再对特性的修改作出相应的反应。这样当下次读取特性值时,读取的总是最新的值。如果两者相同,那就什么也不需要干。3Stored限定符Stored限定符的语法格式为:Storetrue true/falsedefaultnodefaultStored限定符用于指定一个布尔表达式,通过这个布尔表达式的值来控制特性的存储行为14.4.3数组特性所谓数组特性,即特性是个数组,它是由多个同类型的值组成的,其中每个值都有一个索引号,不过跟一般的数组不同的是,一般的数组是自定义类型,可以把数组作为一个整体参与运算如赋值或传递等,而对数组特性来说,一次只能访问其中的一个元素。对于数组特性来说,可以使用Read和Write限定符,但Read利Write限定符只能指定方法而不能是字段,并且ObjectPascal规定,Read限定符指定的方法必须是一个函数,函数的参数必须在数量和类型上与索引变量一一对应,其返回类型与数组特性的元素类型一致。Write限定符指定的方法必须是一个过程,其参数是索引变量再加上一个常量或数值参数,该参数的类型与数组特性的元素类型一致。访问数组特性中的元素跟访问一般数组中的元素一样,也是用特性名加索引号。14.4.4特性的重载和重定义所谓特性重载,就是在祖先类中声明的特性,可以在派生类中重新声明,包括改变特性的可见性(关于类成员的可见性将在后面详细介绍),重新指定访问方法和存储限定符以及缺省限定符等。最简单的重载,就是在派生类中使用指令字Property,其一般格式为:Property;类中可以重新定义一个与祖先类具有相同名称的属性,重定义相当于声明了一个新的属性,该属性隐藏了从祖先类中继承的同名属性。属性声明中是否声明了属性类型是区别覆盖与重定义的唯一途径。如果后代类中声明的属性带有类型,那就是重定义。重定义一个属性必需给出完整的定义。不管在后代类中是通过重定义隐藏还是覆盖祖先类中的属性,属性的调用总是静态的。14.5 类成员的可见性类成员的可见性面向对象编程的重要特征之一就是类成员可以只有不同的可见性,在ObjectPascal中,是通过这么儿个保留字来设置成员的可见性的:Published、Public、Protected、Private、Automated。1Private(私有):在Private部分声明的成员是私有的,它们只能被同一个类中方法访问,对于其他类包括它的派生类,Private部分声明的成员是不可见的,程序员不必知道类实现的细节,只需要关心类的接口部分。2Public(公有):在Public声明的成员是公共的,也就是说,它们虽然在某个类中声明的。但其他类的实例也可以引用,相当于C语言中的外部变量。3Published:在Published部分声明的成员,其可见性与在Public部分声明的成员可见性是一样的,它们都是公共的,即这些成员可以被其他类的实例引用,Published和Public的区别在于成员的运行期类型信息不同。一个Published元素或对象方法不但能在运行时,而且能在设计时使用。4Protected(保护):Protected与Private有些类似。在Protected部分声明的成员是私有的(受保护的),不同的是在Protected部分声明的成员在它的派生类中可见,并且成为派生类中的私有成员。在Protected部分声明的成员通常是方法,这样既可以在派生类中访问这些方法,又不必知道方法实现的细节。4Automated:C+的程序员可能对这个保留字比较陌生,在Automated部分声明的成员类似于在Public部分声明的成员,它们都是公共的,唯一的区别在于在Automated部分声明的方法和特性将生成OLE自动化操作的类型信息。14.6 类类型的兼容性类类型的兼容性一个类类型类与它的任何祖先类型兼容。因此,在程序执行时,一个类类型变量既可以引用那个类型本身的实例,也可以引用任何继承类的实例。14.7 VCL类结构与类结构与Tobject类类14.7.1VCL类结构VCL(VisualComponentLibrary)是Delphi提供的一个可视化组件库。该组件库是一个类库,包含了在Delphi程序设计中所用到的几乎所有的组件类结构。这些类的对象都集成到了Dephi开发环境中(IDE)的组件面板上。有些类是可视的,有些类是不可视的。VCL中主要类之间的继承关系Tobject类是所有其他类的祖先。TObject在System单元声明,该类只定义了少数方法,包括一个基本的构造函数和析构函数。TObjectExceptionTStreamTPersistentTPrinterTListTGraphicsObjectTGraphicTComponententTCollectionTStringsTScreenTmenuTControlTCommonDialogTFieldTGraphicControlTWinControlTFormTScrollingWinControlTCustomControl图14-1 VCL主要类之间的继承关系TPersistent是Tobject类的直接派生类,该类是一个抽象类,主要为它的继承者提供对流的读写能力。TComponent类是TPersistent类的直接派生类,该类是VCL中所有组件类的祖先类。该类定义了所有组件最基本的属性、方法和事件。该组件类中包含可视的和不可视的组件。TControl直接派生于TComponent类,该类是所有在运行时可见的组件类的祖先类,该类中封装了可见组件的运行位置等信息。在Delphi应用程序中使用比较多的TForm类就是TControl类的派生类。14.7.2Tobject类Tobject类是所有VCL类的祖先,对于用户自定义的类也是如此。当用户在声明一个类类型时,如果不指定父类,Delphi将按照缺省情况指明Tobject类为父类。例如,对Tstudent类进行如下的定义:TypeTstudent=Class End;谢谢大家!谢谢大家!结束!结束!
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号