
Delphi教学培训PPTDelphi教程(清华版).ppt
484页Delphi教程教程课程介绍课程介绍§Delphi是著名的Borland公司开发的可视化软件开发环境,自1995年问世以来,带来了程序设计中的一场重大变化Delphi作为第四代编程语言,具有简单、高效和功能强大等特点,这些特点使得它为广大程序开发者所青睐 第1章 Delphi基础知识1.1 Delphi简介简介 Delphi作为一种可视化的编程环境,经历了7代产品的发展历程: ◆ Borland公司在1995年推出了基于Windows平台的Delphi 1◆ Delphi 2新增加的主要功能是对32位编程的支持还增加了如数据模块等多种高效的数据重用功能 ◆Delphi 3对其组件库进行增强 ,完全支持ActiveX组件及其创建 ◆Delphi 4提供了Object Pascal语言的扩展 ,但系统稳定性较差 ◆1999年推出Delphi 5增强了数据库的功能 ◆2001年6月推出Delphi 6 ,增加了大量的新组件,使组件数目达350多个,以满足网络开发的需要 ◆2002年8月推出 Delphi 7 ,在集成开发环境IDE、Web、数据库、编译器、模型生成支持及组件库等很多方面进行了改进 1.2 Delphi7 IDE1.2.1 认识集成开发环境认识集成开发环境 Delphi 7的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的组件面板②双击组件板上的所需组件的按钮,即可将组件添加到窗体的中心位置③按下
可以用鼠标调整组件在窗体设计器上的位置和大小,还可以用对象观察器和代码编辑器来控制组件的外观和行为 3.对象观察器对象观察器 利用对象观察器,可以修改窗体或组件的属性,或者使它们能够响应不同的事件 属性(Property)是一些数据,如高度、颜色、字体等,它们决定了组件在屏幕上的外观事件(Event)则是一种消息处理机制,它能够捕捉某种情况的发生并做出反应,像鼠标单击和窗口打开就是两种典型的事件 对象观察器类包括Properties选项卡和Events选项卡 下图两个对象观察器,左边一个按种类排序,右边一个按名字排序注意:帮助系统是和对象观察器紧密结合在一起的,想了解某个属性或事件的帮助信息,只要在该属性或事件上按下F1 键 图1-5 按种类或名称查看Object Inspector4.代码编辑器代码编辑器 代码编辑器是输入代码来指定应用程序行为的地方,也是Delphi根据应用程序中的组件自动生成代码的地方当向应用程序中加入一个窗体时,Delphi会自动创建一个新的单元,并添加到代码编辑器顶部的标签中5.代码浏览器代码浏览器 以一种树状视图方式显示列在代码编辑器中的单元文件。
通过它,可以方便地在单元文件中漫游或在单元文件中加入新的元素或者把已有的文件改名要记住代码浏览器和代码编辑器有一对一的关系 6.源代码生成器源代码生成器 当对窗体设计器中可视化组件进行操作时Delphi IDE自动生成Object Pascal源代码 当用【File】|【New】|【Application】菜单命令创建一个新的项目时,将看到屏幕上出现一个空白的窗体设计器,同时,代码编辑器中会自动出现一些代码,如下所示:unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs;type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end;var Form1: TForm1;implementation{$R *.dfm}end. 从上述源代码清单中可以看出,窗体对象是从TForm继承下来的。
下面这一行非常重要: {$R*.dfm} Pascal语言中的$R指令用于加载一个外部资源文件dfm文件中包含在窗体设计器中创建的表单的二进制代码其中的“*” 表示与当前单元文件同名的文件 项目文件的扩展名是.dpr 可以选择主菜单下的【Project】|【View Source】命令把项目源文件调入代码编辑器 如下用程序示例的项目文件: program Project1;uses Forms, Unit1 in 'Unit1.pas' {Form1};{$R *.res}begin Application.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正是建立在这种编译器基础之上的 增加了链接和各种缓存策略,尤其是在Visual C++和C++ Builder中3.编程语言的功能及其复杂性编程语言的功能及其复杂性 汇编是一种最有力的语言,即便是用汇编开发最简单的应用程序,难度也非常大,还可能一无所获 4.丰富的丰富的VCL VCL是Delphi7最重要的组成部分,包含不同种类的组件 1.2.4 帮助的使用帮助的使用(1)当遇到问题时,可以尝试按下【F1】键,一般情况下Delphi都会准确地定位 (2)如果需要浏览系统的一些帮助内容,比如说对象Pascal语言,可以通过菜单【Help】|【Delphi Help】命令打开【帮助主题:Delphi Help】对话框,在目录标签中展开相关条目,进行系统学习。
3)当使用Windows API函数时,通过【Help】|【Delphi Help】打开的【帮助主题:Delphi Help】对话框中,在【索引】选项卡中可能查不到有关的帮助信息,有两种解决办法: 一种办法是在代码编辑器中输入Windows API函数的名称,然后将光标定位到该名称中,接着按下【F1】键 另一种办法是通过【Help】|【Windows SDK】打开帮助文件Win32.hlp,在【索引】选项卡中查找4)帮助文件打开时,要浏览相关内容,可以按下工具栏中的【>>】或【<<】按钮,通常这样是在同一个主题中浏览通过一些See Also热链接,也可以查看相关的帮助内容5)在组件栏上右击,通过快捷菜单的Help命令,可以查看有关组件的帮助内容 (6)在Delphi集成开发环境中,通过【Help】|【Customize】命令可以打开【Open Help】窗口,在该窗口中可以对帮助文件进行管理,其中包括帮助内容、帮助索引、链接位置和帮助工程文件等的管理1.3 简单简单Delphi程序设计程序设计 Delphi 7编写应用程序包括新建应用程序、设置窗体属性、添加组件、设置组件属性、添加事件、编写事件响应代码、编译运行等1.新建应用程序新建应用程序 启动Delphi 7,选择【File】|【New】|【Application】菜单,新建一个应用程序。
2.设置窗体属性设置窗体属性 单击【Object Inspector】,在对象观察器中打开【Properties】选项卡,单击Caption属性右侧,输入窗体的新标题“窗口”3.向窗体中添加组件向窗体中添加组件 单击Standard选项卡上的Button组件,将鼠标指向窗体中的任意位置(标题栏除外),单击鼠标,即可把Button1组件放入窗体中或者直接双击组件面板上的Button组件,也可以在窗体中添加一个Button1组件4.设置组件属性设置组件属性 选中组件,单击Object Inspector的标题栏以激活对象观察器,并选择Properties选项卡在对象观察器中单击要设置的属性,进行属性设置5.添加事件添加事件 选中要添加事件的组件,激活对象观察器,并选择Event选项卡,在对象观察器中单击要添加的事件,在其右侧输入事件的响应函数名称,然后回车即可 6.编写事件响应代码编写事件响应代码 选定要编写事件响应代码的组件 ,打开Event选项卡,双击要编写响应代码的事件右侧的空白部分,进入代码编辑窗口 procedure TForm1.Button1Click(Sender: TObject); //单击放大按钮,窗口将放大beginform1.Height:=form1.Height+10; //窗口高度10form1.Width:=form1.Width+10; //窗口宽度10end;procedure TForm1.Button2Click(Sender: TObject); //单击缩小按钮,窗口将缩小单击缩小按钮,窗口将缩小Beginform1.Height:=form1.Height-10; //窗口高度减窗口高度减10 form1.Width:=form1.Width-10; //窗口宽度减10end; procedure TForm1.Button3Click(Sender: TObject); //关闭窗口beginclose; //退出end;7.编译运行程序编译运行程序图1-6 例程运行界面1.4 Delphi上机步骤上机步骤1.启动程序启动程序2.添加组件添加组件在窗体设计器中添加如图1-7所示的各组件。
3.设置组件属性设置组件属性4.编写代码编写代码组件属性设置完成后,编写如下代码:图1-7 窗体布局图§procedure TForm1.Edit1Change(Sender: TObject); //编辑框1中的内容改变时的事件§var§stringlength:integer; //定义整型变量,记录编辑框1中字符的个数§begin§stringlength:=edit1.GetTextLen; //得到编辑框1中字符个数§edit2.Text:=inttostr(stringlength); //编辑框2显示编辑框1中字符个数§end;§procedure TForm1.Button1Click(Sender: TObject); //关闭窗口§begin§close;§end;5.保存工程保存工程保存工程分为保存单元文件和保存项目文件两步 第一步是保存单元文件,单击工具栏上的Save按钮,将打开Save Unit1 As窗口,默认的单元文件名是“Unit1.pas”,以“.pas”为扩展名,单元文件名根据需要可以另取 第二步是保存项目文件,单击工具栏上Save Project1 As按钮,默认的项目文件名是“Project1.dpr”,工程文件名根据需要可以另取以“.dpr”为扩展名 6.运行工程运行工程保存工程结束后即可运行工程 小结小结 在本章中,主要介绍了Delphi的产生和发展,Delphi的特点,使用Delphi进行程序设计的一些基础知识。
本章的重点是Delphi的开发环境和开发方法,通过实例介绍了Delphi的程序设计和上机操作的一般步骤图1-10 工程运行界面图第第2章章 Delphi语法基础语法基础 2.1 保留字与标识符保留字与标识符2.1.1 标识符标识符 标识符是Object Pascal语言中各种成分 的名称,这些成分包括变量(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.自定义标识符 程序员根据程序设计的需要,自己定义的常量、变量、类型、函数、过程等所取的名字自定义标识符可以由任意长的一个不带空格的字符串组成,包括字母A~Z 、a~z 、数字0~9 和下划线“_”等。
定义标识符需要遵循以下规则:§(1)标识符不区分大小写;§(2)标识符只能以字母或下划线开头,不能以数字开头;§(3)标识符可任意长度,但只有前225个字符有效;§(4)标识符中间不允许有空格; §(5)不允许使用Object Pascal语言的保留字作为标识符 3.限定标识符 在Delphi程序中可引用多个单元,而各个单元中全局变量、函数、过程等可能会同名,在引用时需用限定标识符来区分它们:§Var §Y:real;§Y:=System.cos(pi); 其中System称为限定符 ,而System.cos称为限定标识符 2.1.2 保留字保留字 保留字由系统规定具有特定意义,不能被重新定义或作他用,定义了65个保留字 注意:§1.单词at和on具有特殊含义,不要与它们同名§2.保留字和Object Pascal一样不区分大小写§3.Delphi集成开发环境的代码编辑器中,黑体显示保留字和指令字,定义时不要与这些黑体字一样 Object Pascal的保留字 and array as asm begin case class const constructor destructor dispinterface div do downto else end except exports file finalization finally for function goto if implementation in inherited initialization inline interface is label library mod nil not object of or out packed procedure program property raise record repeat resourcestring set shl shr string then threadvar to try type unit until uses var while with xor 2.1.3 指令符指令符 指令字只在特殊的程序位置、或当上下文关联时有意义的程序区段有自己特殊的意义,而在其他场合,用户可对其重新定义,即可将其定义为标识符,Object Pascal不会指示出错,当用户重新定义这些指令字后,在作用域内它们就失去了原来的意义了。
Object Pascal中规定的指令符有39个说明:指令符private、protected、public、published和automated在定义对象类型时也作为保留字,而在其他场合则作为指令符2.1.4 注释注释 注释可增加程序的可读性和可维护性 Object Pascal语言中注释有三种形式:§1.组合符号“{”与“}”的成对使用表示它们之间的内容为注释部分§2.组合符号“( *”与“* )”的成对使用表示它们之间的内容为注释部分§3.符号“// ”的单个使用表示所在行的该符号之后的内容为注释 注意:§1.注释符“{”与“}”、“( *”与“* )”在使用时不支持注释的嵌套,而且必须成对使用§2.对于单行和少量几行注释使用符号“// ”,对于大块注释使用“{”和“}”或“( *”和“* )”§3.有时可利用注释在代码中形成一个醒目标志§4.在注释符“{”或“( *”后紧接着是一个美元符号“$”时,表示该句是一个编译器指令,它与普通的注释不同,通常用来对编译过程进行设置2.2 数据类型数据类型 描述客观事物的数、字符以及所有能输入到计算机中并被计算机程序加工处理的符号的集合称为数据。
数据类型可以分为标准数据类型及高级数据类型等,还可以通过数据类型声明语句在预定义数据类型的基础上定义新数据类型说明:§1.标准数据类型属于Object Pascal内部约定的数据类型,无需定义就可以直接使用§2.高级数据类型体现了特殊的数据结构,在使用之前必须由用户自己定义§3.数据类型中整型、字符型、布尔型、枚举型和子界型被称为顺序类型,其取值是一个有序集合,每一个可能取值都与顺序有关2.2.1 数值型数据数值型数据 数值型数据可分为整数类型和实数类型1.整数类型 整数类型是存储整数数据的类型 ,分为基本整形和一般整形基本整形:短整型 、小整型 、长整型 、64位整型 、字节型 、字型 、长字型 一般整形:整型 、序数型 注意:尽量使用一般整型Integer和Cardinal,可以最大限度发挥CPU和操作系统的性能 2.实数类型 实数类型是存储实数数据的类型 ,分为基本实型和一般实型基本实型:单精度实型 、扩展型 、双精度实型 、货币型 一般实型:实型 注意:Real 类型与Double 类型完全等价 Currency类型至少有4位有效的小数位 2.2.2 字符型数据字符型数据 Object Pascal中的字符型数据可以分为字符型和字符串型2类7种。
1.字符类型 Object Pascal包括3种形式的字符型数据 类型类型 名称名称 字节数字节数 取值范围取值范围 Ansi字符型 AnsiChar 1扩展ANSI字符集 宽字符型 WideChar 2UniCode字符集 字符型 Char 1(2)扩展ANSI字符集 说明:(1)前2种为基本字符类型后一种为一般类型2)Char与AnsiChar完全等价,但Char常用 2.字符串类型 字符串类型是存储字符串数据的类型 ,Object Pascal包括了4种形式的字符串型数据 类型类型 名称名称 最大长度最大长度 所需内存空间所需内存空间 短字符串型 ShortString 255个字符 2~256B 长字符串型 AnsiString 231个字符 4~2GB 宽字符串型 WideString 230个字符 4~2GB 字符串型 String 231个字符 4~2GB 说明:(1)AnsiString类型的定义是动态分配的,内容由AnsiChar类型的字符组成,长度仅受可用内存空间的限制,以空字符Nul作为结尾2)String字符串类型,既可以是ShortString类型也可以是AnsiString类型,默认定义是AnsiString类型。
2.2.3 布尔型数据布尔型数据 布尔型数据用于关系运算和条件语句的逻辑运算,包括4种形式的布尔型数据 说明:(1)后3种类型是为了兼容其他语言而设置的,编程时应尽量使用Boolean类型2)Boolean取值为False和True两个符号常量类型类型 名称名称 字节数字节数 取值取值 布尔型 Boolean 1只能为0(False)或1(True) 字节布尔型 ByteBool 10(False)或非0(True) 宽布尔型 WordBool 20(False)或非0(True) 长布尔型 LongBool 40(False)或非0(True) 2.3 常量与变量常量与变量2.3.1 常量常量 常量即在程序的执行过程中其值不能改变的量 常量有两种,一种是常量值本身,也称为直接常量;另一种是要用声明定义的标识符表示的常量,也称为声明常量声明常量又可以分为符号常量和类型常量1.直接常量 直接常量是指在程序中直接引用的常数,如整型常数、实型常数、字符型常数、字符串型常数和布尔型常数 2.声明常量(1)符号常量 在程序中,某一个常数反复多次出现,可以定义一个标识符来代表该常数,这个标识符就是符号常量,其值在定义后不会改变。
也称纯常量定义符号常量使用常量说明语句,其语法格式为:Const <常量名1>=<常量值1>; … <常量名n>=<常量值n>;其中Const是保留字,表示常量定义段开始 注意:●保留字Const可单独一行也可与常量一行●不能在程序中给常量另行赋值,否则将导致语法错误●Delphi由常量值判断常量名属于哪种类型2)类型常量 类型常量用于保存数组、记录、过程以及指针等类型的值,不能出现在常量表达式中在默认的编译器状态下,类型常量的值可改变,但当在程序中加入编译命令{$j-}时,则类型常量的值在运行期就无法改变 声明类型常量的语法规则为:Const <类型常量名>:<类型>=<常量值>; 其中类型是除文件型和可变型的所有类型,常量值可以是和类型相应的常量表达式 2.3.2 变量变量1.变量的声明 变量在单元、函数或过程的声明部分进行声明,声明的位置决定了变量的作用域声明包括两部分:变量名和它所属的类型,变量声明的语法格式为:Var <变量名1>:<类型名1>;…<变量名n>:<类型名n>;当多个变量具有相同数据类型时,格式如下:Var <变量名1>,<变量名2>,…<变量名n>:<类型名>;其中,Var是保留字,表示变量声明段的开始;同类型的<变量名>可超过一个,<变量名>间用“,”分隔;<类型名>可以是基本数据类型或是由用户定义的高级数据类型; 2.变量的使用 一旦声明了一个变量应及时对它进行初始化,最简单方法就是给变量赋值,在表达式中使用变量。
2.4 运算符与表达式运算符与表达式 按照操作数数目的多少来分,运算符分为下面两类:单目运算符和双目运算符单目运算符一般放在操作对象的前面,双目运算符都放在两个操作数之间 表达式是表示某个求值规则的运算公式,由运算符和配对的圆括号将常量、变量、函数、对象等操作数以合理的形式组合而成 2.4.1 算术运算符与算术表达式算术运算符与算术表达式1.算术运算符 算术运算符对浮点数和整数进行加、减、乘、除和取模运算,取正“+”和取负“-”是单目运算符,其他均为双目运算符§说明:(1)+、-、*运算中,参加运算的数可以是整型和实型,结果自动向精度高的类型转化2)参加除法运算“/”,结果都是实型的商3)参加整数除法“Div”和求余运算“Mod”的数必须是整型,结果也是整型数,符号与被除数的符号相同,小数部分被舍去4)在表达式a/b、a Div b和a Mod b中,如果b的值为0,将会触发一个错误 2.算术运算符的优先级 同级运算自左至右,如果含有括号,则先计算括号内表达式的值3.算术表达式 将数学式改写为算术表达式,考虑三个问题:一是语法,二是优先级,三是类型 优先顺序优先顺序 运算符运算符 1+、-(取正、取负) 2*、/(法、除法) 3Div、Mod(整除、求余) 4+、—(加法、减法) §说明:(1)数学式中省略的运算符和表示函数参数的括号必须添加上去 (2)必须注意优先级的处理 ,恰当利用标准函数 ,注意数据类型。
2.4.2 逻辑运算符与布尔表达式逻辑运算符与布尔表达式 逻辑运算符可分为布尔运算符、位运算符和关系运算符1.布尔运算符 只能对两个布尔型操作数进行运算,结果仍为布尔型,True或False 其中,NOT是求“非”,为一元运算符 ; AND是求“与”,OR是求“或”,XOR是求“异或”,均为二元运算符 2.位运算符运算符运算符 操作举例操作举例操作数类型操作数类型 结果类型结果类型 功能说明功能说明 NOT NOT x integer integer 即按二进制形式将每位求反AND a AND b integer integer 将两者相对应的位进行AND运算OR a OR b integer integer 将两者相对应的位进行OR运算XOR a XOR b integer integer 将两者相对应的位进行取XOR运算,两者不同时结果为1 SHL a SHL b integer integer 将a的二进制值向左移动b位,左移一位相当于乘2 SHR a SHR b integer integer 将a的二进制向右移动b位,右移一位相当于除2 注意:右移操作时原值的低位丢失,高位补0;左移操作时原值的高位丢失,低位补0。
3.关系运算符关系符关系符 操作操作 操作数类型操作数类型 结果类型结果类型 = 等于 简单类型,字符串或可变类型,类,类引用,指针,集合类型 Boolean <> 不等于 简单类型,字符串或可变类型,类,类引用,指针,集合类型Boolean < 小于 简单类型,字符串或可变类型 Boolean > 大于 简单类型,字符串或可变类型 Boolean <= 小于等于 简单类型,字符串或可变类型 Boolean >= 大于等于 简单类型,字符串或可变类型 Boolean 4.布尔表达式 布尔表达式由布尔运算符和布尔类型的操作数所组成,包括关系运算表达式和运算结果为布尔类型的函数,如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;§var§ I:integer;§begin§… //语句§ if(I=0) then goto Aa;§ … //语句§Aa:begin§ … //语句§ end;§end;2.类型声明语句 在Object Pascal中,所有的变量必须是某种特定的数据类型,类型决定了它所能包含的数值和可进行的操作,用类型声明语句可以定义新的数据类型例如:TypeTmyDim:Array[1..10,1..5] of Double;3.过程声明语句 过程可以被看成一段小程序,用来实现某种特定的目标,在完整的程序中它被当作一个语句来执行在建立过程之前应先声明procedure NumString(N:Integr;Var S:string);4.函数声明语句 函数与过程相似,主要区别在于函数必须有返回值,函数的声明参见下面的语句,其中,最后的Real表示函数的返回数据类型。
Function Power(X:Real;Y:Integer):Real;2.6.3 赋值语句和程序的顺序结构赋值语句和程序的顺序结构1.赋值语句赋值语句的语法格式为:<变量名>:=<表达式>;2.类型兼容 类型兼容是指数据类型不完全相同的量之间能进行的运算和赋值操作 3.利用赋值语句给对象属性赋值 由于属性总是归属于对象才有实际意义,所以引用属性时用符号“.”来连接表示其隶属关系如组件Editl的字体的颜色属性表示为:Editl.Font.Color 4.顺序结构 顺序结构是最简单、最常用的结构在该结构中,各操作块按照出现的先后顺序依次执行,不产生程序流程的其他转移它是任何程序的主体结构,即使在选择结构或循环结构中,也常以顺序结构作为其子结构通常由若干个赋值语句或其他简单语句构成 2.6.4 条件语句和程序的选择结构条件语句和程序的选择结构 实现选择结构的是IF语句和Case语句,这两种语句又称条件语句,条件语句的功能就是根据表达式的值有选择地执行一组语句§1.if语句 通过条件的布尔表达式值选择执行路径 if <条件> then [<语句1>][else <语句2>]; if语句分为简单条件语句和复合条件语句。
简单条件语句的<语句>中不包含其他的条件语句 如果在If语句格式中的<语句1>或<语句2>本身又是一个If语句,则称为If语句的嵌套,嵌套的If语句又被称为复合条件语句 §2.Case语句 Case语句用来实现多分支选择结构Case语句描述了多路择一的功能,它根据“选择器表达式”的值决定执行相应的语句Case语句的语法格式为: case <选择器表达式> of<情况常量表1>:<语句1>;…<情况常量表n>:<语句n>;[else <其他语句列>;]end;说明:<选择器表达式>的值必须是顺序类型 2.6.5 循环语句和程序的循环结构循环语句和程序的循环结构 从某处开始有规律地反复执行某一程序块的现象称为“循环”,完成这一功能的程序结构为“循环结构,”而其中重复执行的程序块称为“循环体”循环结构语句有3种,它们分别是:While 语句、Repeat语句以及For语句 1.While语句 While语句属于前测型循环结构首先判断条件,根据条件决定是否执行循环,执行循环的最少次数为0其语法格式为:§while <条件> do§[<循环体>];说明:可在(循环体)中任何位置Break语句来终止While 循环,Break 语句通常位于IF语句后。
可在循环体中任何位置放置Continue语句,以便在整个循环体没有执行完就重新判断(条件),以决定是否开始新的循环Continue语句通常位于IF 语句之后 2.Repeat语句 Repeat语句属于后测型循环结构,首先执行循环体,然后判断条件,根据条件决定是否继续执行循环,执行循环的最少次数为1§repeat§ [<循环体>]§until <条件>;3.For语句 若知道要执行多少次循环时,则使用For循环结构For循环使用一个循环变量,每重复一次循环之后,循环变量的值就会自动增加或者减少For语句的语法格式为: §for <循环变量>=<初值>{to|downto}<终值> do§[<循环体>]; 说明:<循环变量>只能是顺序类型,TO表示计数器递增,DownTo表示计数器递减 4.循环的嵌套 循环语句的循环体中仅包含了简单语句,称为单重循环 如果在循环体中又包含了另一个循环结构,则称为多重循环,又称为循环的嵌套 在循环体中的嵌套称为内循环,外部的循环称为外循环多重循环嵌套根据循环结构嵌套层数可以分为二重循环、三重循环等 5.循环的中断 特殊情况下,需要中断正在执行的循环,可以使用break语句或Continue语句。
可以放在循环体的任意位置,通常放在If语句之后 执行Break语句的结果是:跳出整个循环,执行<循环体>之后的语句 执行Continue语句的结果是:跳出本轮循环,然后判断循环条件是否成立,再决定是否开始新一轮的循环小结小结 本章主要介绍了Object Pascal的最基本的语法,主要包括基本词法、基本数据类型、常量与变量、运算符与表达式、常用函数与过程、语句等第3章 常用组件 Delphi 7.0的组件板上含有27个选项卡,总共包括350多个组件,如图所示 :3.1 窗体窗体 3.1.1 Form组件组件 窗体是应用程序的操作界面,是放置组件的基础窗体由标题栏、工作区和边界组成 图3-1 组件板和选项卡运行界面图1.Form的主要属性 窗体组件(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组件,即可完成界面设计 添加代码:procedure TForm1.Button1Click(Sender: TObject); //创建按钮事件begin //关键分析 form2.show; //调用Show方法显示Form2窗体end;procedure TForm1.Button2Click(Sender: TObject);begin form1.Close;end;程序分析:编译上述工程时,系统会弹出出错提示信息,单击“Yes”按钮,Delphi将自动在Unit1单元中添加对Unit2单元的引用。
(2)动态态创建新窗体 在需要某个窗体时,临时创建它,使用后将其立即释放,这种称为窗体的动态创建 3.1.2弹出对话框窗体弹出对话框窗体 Delphi提供内部对话框有两种 第一种:信息输出对话框Showmessage过程、ShowMessageFmt过程、MessageDlg函数、MessageDlgPos函数、CreateMessageDialog函数 第二种:信息输入对话框InputBox函数、InputQuery函数1.ShowMessage过程 其语法格式为:ShowMessage(<信息内容信息内容>);; 2.ShowMessageFmt过程的语法格式为: ShowMessageFmt(<信息内容信息内容>,,<参数组参数组>);;3..MessageDlg函数函数其语法格式为:其语法格式为:<变量变量>=MessageDlg(<信息内容信息内容>,<类型类型>,<按钮组按钮组>,HelpCtx); 4.MessageDlgPos函数 其语法格式为:<变量变量>=MessageDlgPos(<信息内容信息内容>,<类型类型>,<按钮按钮组组>,HelpCtx,X,Y); 可以指定对话框的显示位置坐标:X,Y。
5.CreatMessageDialog函数其语法格式为:<变量变量>=CreatMessageDialog (<信息内容信息内容>,<类型类型>,<按钮组按钮组>);6..InputBox函数函数其语法格为:其语法格为:<变量变量>=InputBox (<对话框标题对话框标题>,<信息内容信息内容>,<默认默认内容内容>);7.InputQuery函数其语法格式为:<变量变量>=InputQuery (<对话框标题对话框标题>,<信息内容信息内容>,<字字符串变量符串变量>);3.2 输入显示类组件输入显示类组件3.2.1 Edit组件 编辑框(Edit)是一种通用组件,既可以输入文本,又可以显示文本,编辑框组件位于Standard组件板中 图3-7编辑框Edit 运行界面图1.Edit的主要属性 (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.2 Label组件 标签组件位于Standard组件板中 图3-8 标签Label 运行界面图1.Label的主要属性 (1)Caption属性:用来显示标签的文本 (2)ShowAccelChar属性 :决定是否将&作为作为热键字符的标记 (3)AutoSize属性:决定标签是否自动随文本的变化而改变大小 4)Alignment属性:决定对齐方式 5)Layout属性:控制文本显示在标签的位置 (6)WordWrap属性:控制是否折行显示 (7)Transparent属性:决定背景是否透明 (8)FocusControl属性:用来获得焦点组件名 3.2.3 Memo组件组件备注框组件位于Standard组件板中。
1.Memo的主要属性 备注框在Delphi中用Tmemo类处理,Tmemo类是Tedit类的衍生类,为了处理多行文本,Tmemo类还增加了一些新的属性1)CaretPos属性:得到光标在编辑区中位置 (2)Lines属性:用来存放Memo对象的文本 (3)Modified属性:确定文本是否被改动过 图3-10 备注框 Memo运行界面图(4)ScrollBars属性:决定备注框是否具有滚动条 5)WordWrap属性 :设置文本是否能够换行 6)WantReturns属性:用来设置备注框是否能插入“回车”键 (7)WantTabs属性:用来设置备注框是否能插入“Tab”键 2.Memo的使用【例3.4】利用编辑框,把编辑框中的文本输入到Memo中1)界面设计 创建一个新的工程,在窗体中添加1个按钮Button1组件、一个编辑框Edit1组件和一个备注框Memo1组件,各组件的属性设置如图:(2)程序设计procedure TForm1.Button1Click(Sender: TObject);begin Memo1.Lines.Add(Edit1.Text); edit1.Text:=''; edit1.SetFocus; end;图3-12 Memo示例运行界面图procedure TForm1.FormActivate(Sender: TObject);begin edit1.SetFocus;end;3.2.4 MaskEdit 组件组件 它限制用户在所定义的位置输入要求输入的符号。
掩码编辑框(MaskEdit)组件位于Additional附加组件板”中1.MaskEdit常用属性图3-13 Memo示例运行界面图(1)EditMask属性 EditMask属性用来控制用户输入数据格式的掩码字符串,掩码字符串EditMask属性分为三个部分,用分号分隔第一部分是掩码字符串的主要部分,它确定了数据的格式;第二部分决定是否将掩码中的字符串作为数据的一部分,0表示不作为数据的一部分,1表示作为数据的一部分,它将影响属性;第三部分指出在掩码中用来代表未输入数据的字符2)EditText属性:用来返回用户输入的数据 3.3按钮类组件按钮类组件3.3.1 Button组件组件 Button按钮在Delphi 7.0组件板Standard选项卡中 1.Button的主要属性(1)Caption属性:来制定按钮所显示的文字2)Cancel属性:决定改按钮是否为取消按钮 ,缺省值为False3)Default属性:用来决定改按钮是否为默认按钮,缺省值为False 图3-16 Button基本按钮运行界面图(4)ModalResult属性:用来决定模式窗体如何被关闭 2.Button的事件 Button组件常用的事件如表所示。
在下述两种情况下,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工具栏的风格默认值为False4)GroupIdex属性:该属性默认值为0,表示不与其他SpeedButton成组 3.4复选框、单选按钮和单选按钮组复选框、单选按钮和单选按钮组3.4.1 CheckBox组件 位于Delphi 7.0组件板Standard选项卡中 图3-21 复选框CheckBox 复选框CheckBox具有选中和未被选中两种状态,未选中状态 ,选中状态 。
还有一种不确定状态,表示既非选中又非未选中1.CheckBox的主要属性(1)Checked属性:用于表明CheckBox是否被选中 (2)State属性:属性State进一步确定CheckBox状态有3种值:cbChecked、cbUnchecked和cbGrayed,分别为选中、未选中和不确定3)AllowGrayed属性:为True时,复选框有3种选择:为False时,只有选中和未选中状态3.4.2 RadioButton组件 位于Delphi 7.0组件板Standard选项卡中RadioButton的主要属性有: Checked属性:表明CheckBox是否被选中RadioButton有两种状态,如果当Checked属性为True时,表示选中状态,如果当Checked属性为False时,表示未选中状态3.4.3 RadioGroup 组件 位于Delphi 7.0组件板Standard选项卡中 图3-23单选按钮RadioButton1.RadioGroup的主要属性(1)Columns属性 属性Columns用于设置单选按钮组中按钮的列数范围1~16,默认值为12)Items属性:用于设置各种单选按钮标题。
3)ItemIndex属性:单选按钮组中被选中按钮(从0开始)的序号默认值为-1,表示组中按钮均未被选中 图3-25单选按钮组RadioGroup 3.5列表框、组合框列表框、组合框3.5.1 ListBox组件 当列表框不能同时显示所有选择项时,将自动加上一个垂直滚动条,使用户可以上下滚动列表框,以查阅所有的选项列表框位于组件板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.2 ComboBox组件 兼有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选项卡中 。
1.ScrollBar主要属性、方法与事件(1)LargeChange属性:当用户单击滚动条时,滚动距离由LargeChange属性设置,默认1 (2)Max、Min属性:设置滚动条可滚动的范围 图3-31 滚动条 Scrollbar(3)PageSize属性:当用户按键盘上的PageUp或PageDown键时,滚动条滚动的距离是由PageSize属性设置的,默认是1 (4)Position属性:设置或返回滚动条中小方块的位置 (5)SmallChange属性:是用户按滚动条两端的箭头时滚动条的距离,默认值是16)SetPaxams方法:该过程相当于分别设置Position、Max和Min属性7)OnScroll事件:第三个参数返回滚动条小方块的位置,第二个参数返回滚动条的状态 3.6计时器计时器 Timer组件位于System组件板中,如图所示,属于非可视化组件,在设计时显示为一个小时钟图标,而在运行时则不可见了,冲用来做一些后台处理1.Timer组件的主要属性与事件 (1)Enanled属性:为Ture时,定时器开始工作,为False时定时器暂停工作 (2)Interval属性:用来设置定时器触发周期 。
图3-32 Timer计时器(3)OnTimer事件:Timer组件只提供一个事件,即OnTimer该事件以Interval属性设置的频率被触发3.7对话框组件对话框组件3.7.1 Opendialog组件 用于打开一个已经存在的文件,用户选择某一文件,其所在的驱动器、文件夹、文件名以及扩展名将被赋予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.2 Savedialog组件 用于提供一个另存为对话框,用户输入某一文件,其所在的驱动器、文件夹、文件名以及文件扩展名将被赋予SaveDialog的filename属性Savedialog组件位于Dialogs组件板如图3-34的所示的第二个组件3.7.3 Fontdialog组件 用于提供一个字体对话框,用户可以选择需要的字体名称、样式、大小、效果及字体颜色等,这些选择将被赋予Fontdialog的Font属性Fontdialog组件位于Dialogs组件板如图3-34的所示的第五个组件3.7.4 Colordialog组件 用于提供一个颜色对话框,用户可以选择需要的颜色等属性,这些选择将被赋予Colordialog的Color属性Colordialog组件位于Dialogs组件板如图3-34所示第六个组件3.8 Win3.1组件组件3.8.1 FileListBox组件 用于显示指定目录文件名滚动列表,位于如图3-38所示Win3.1组件板中第八个组件FileListBox组件的主要属性:(1) Directory属性:设置当前文件目录,显示的文件列及表自动更新显示文件目录的文件。
2) Drive属性:用于设置当前驱动器盘的号 ,当前属性值改变时,Directory属性值自动改变为新的驱动器下的当前目录图3-38 Win3.1组件板(3)ExtenderdSelect属性 :若为Ture则可按着
(3) Drive属性:用于设置当前的驱动器盘号,当该属性值改变时,Drive属性值将自动改变为新的驱动器下的当前目录 (4) FileList属性:用于将目录列表链接至文件列表,当目录列表中的目录改变时,文件列表会自动进行更新3.8.3 DriveComboBox组件 用于显示一可选驱动器下拉列表,该组件位于如图3-38所示中第十个组件DriveComboBox组件主要的属性:(1)Dirlist属性:用于将本组件链接至目录列表 ,如驱动器改变,目录列表会自动更新2) Drive属性:用于存放当前的驱动器盘号 (3) TextCase属性:用于决定驱动器盘号使用大写字母还是小写字母 3.8.4 FilterComboBox组件 用于显示一可选过滤器下拉列表,供用户选择,位于如图3-38所示中第十一个组件FilterComboBox组件的主要属性如下: (1)FileList属性:用于将本组件链接至文件列表,如当前的文件类型改变,文件列表会自动进行更新2) Filer属性:用于设置各种过滤文件的类型 (3) Mask属性:用于存放所选的过滤类型的对应 3.9 菜单菜单 一个Windows引用程序,它往往需要制作标准的菜单界面,包括主菜单Mainmenu、弹出式菜单Popmenu两种。
3.9.1 MainMenu组件 主菜单也称为菜单栏,其中包括一个或多个选择项称为菜单项当单击一个菜单项时,包含子菜单项的列表即被打开主菜单位于组件板Standard选项卡中如图所示: 图3-40主菜单 MainMenu1.菜单编辑器 打开一个新的窗体,在其中添加一个MainMenu组件,即可产生主菜单项,菜单项的设置可以通过双击MainMenu组件或右键单击MainMenu组件,在弹出的快捷菜单中选取Menudesigner项,或者选择MainMenu组件Items属性,单击右端 按钮,可打开菜单项编辑器,并产生一个空菜单项,如图所示图3-41菜单项编辑器 载对象编辑器中,Caption属性输入“&S设置”,表示可按钮【Ctrl】+【S】或【Alt】+【S】键来选择此菜单项,其中“&”符号后的第一个字符为加速字符若输入“-”则表示建立菜单分割线将菜单项分组 按【Insert】键则插入一个新的空白菜单项,若按【Delete】键则删除一个菜单项 在Delphi中建立子菜单时,可选择要产生子菜单的菜单项,然后按【Ctrl】+【→】键,便产生下一级子菜单项 2.MenuItem的主要属性(1)Name属性:用于设置菜单组件的名称。
(2)Caption属性:显示菜单组件的标题 (3)Checked属性:设置为True时,相应的在菜单项边上加上选择标志“√”属性设置为False时,则无显示,默认值为False (4)Enabled属性:默认值为True,表示可以响应用户事件,若设置为False,则无法相应用户事件,并且相应的菜单项会变灰 (5)Vsible属性:确定菜单项是否显示,True则显示,False则隐藏6)ShortCut属性:设置该菜单项的热键 3.9.2 PopupMenu组件 应用系统中,对弹出式菜单的支持是一种流行的方式,就是我们通常使用的右键菜单,当用户在不同的地方点击鼠标右键,就弹出不同的菜单项主菜单位于组件板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表示在过程内部不能改变形参的值不带这两个参数的形参传递方式为值传递,即过程内部对形参值的改变将不会反映到实参中过程的定义可以有类似下面的两种形式:§procedure GetSum(V1,V2:Integer;var Sum:Int64);§procedure ProcNoPara;2.自定义过程的创建和使用 一般可以创建两种自定义过程。
一种是仅能本单元中使用,对其他的单元文件该过程不可见;另外一种就是还可以在其他单元中使用的公共过程要创建能在其他单元中使用的过程,必须将过程首部声明在单元的公共接口部分(Interface)中如果是仅在本单元内部使用的自定义过程,则必须先创建才能被使用 4.2 函数函数 函数是有返回值的子程序,一般通过函数名或一个系统预定义的隐含变量Result返回函数的值在Delphi中有两种函数,内部函数和自定义函数 4.2.1标准函数 标准函数是系统内部已经定义好的函数不能改变标准函数的参数以及返回值类型其定义形式如下: function StrToInt(const S: string): Integer; 表明该函数接受一个常量参数,并返回一个整型值4.2.2 自定义函数1.函数的定义 一般的语法格式为:§Function <函数名函数名> [(<形参表形参表>)]:返回类型;返回类型;§[局部声明局部声明]§Begin§[<语句序列语句序列>]§End ; 自定义函数含有一个以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但不能调用A32)内层子程序可以调用外层的子程序而且允许隔层调用如A2B可以调用A2,A3调用A1(3)同一层的子程序,允许后定义的子程序调用先定义的子程序,如A2C可以调用A2B但是A2B不可以调用A2C4)如果需要调用同层中后定义的子程序,必须用保留字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 赋值兼容与调用约定 赋值兼容是指变量可以进行赋值或进行参数传递当两个类型要进行赋值操作而又不满足赋值兼容时,将产生编译错误。
Object Pascal 提供了五种过程和函数的调用方式,分别为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>,<标识符标识符2>,…<标识符标识符n>);说明:(1)type是系统的保留字,表示定义高级数据类型的开始(2)<标识符>表示该类型数据中的元素,圆括号中列出了该类型数据的所有取值,这些取值又称为枚举常量3) 同一个枚举常量不允许重复出现在同一个枚举类型定义中,也不允许同时出现在不同的枚举类型定义中2.枚举类型变量的声明 其声明的格式与其他类型变量的声明完全相同 例如:Var C: Color;; 该语句声明了一个枚举类型Color的变量C5.1.2枚举类型的运算1.使用函数 Object Pascal为枚举类型定义了5个枚举函数,可以进行特殊的运算 说明:(1)枚举类型定义语句中列出的每一个枚举常量都对应一个唯一的序数(整数),称为枚举序数,在缺省情况下,列出的第一个枚举常量对应枚举系数0,以后依次为1、2、3…枚举函数枚举函数 功能功能 调用格式调用格式 Ord 求枚举系数 Ord(枚举常量或枚举变量) Pred 求前趋值 Pred(枚举常量或枚举变量) Succ 求后继值 Succ(枚举常量或枚举变量) Low 求第1个枚举常量 Low(枚举类型名) High 求最后1个枚举常量 High(枚举类型名) (2)在定义枚举类型时,排在某枚举常量前一位的枚举常量称为该枚举常量的前趋值,后一位的称为后继值。
第一个枚举常量没有前趋值,最末一个枚举常量没有后继值 (3)由于每个枚举常量都对应一个枚举系数,所以枚举常量的序数可以进行算术运算,结果类型为整型但枚举常量之间不能直接进行算术运算,需要先转换为枚举序数2.关系运算 由于每个枚举常量对应一个唯一的序数,因此可以在枚举常量之间进行关系运算如在上述定义中,sun
5.3 集合类型集合类型 集合结构是指具有相同性质的对象的全体,构成集合的每个对象称为集合的元素 注意:(1)集合中的元素是互异的 、无序的 2)集合元素个数不能超过256个3)元素与集合的关系是“属于”或“不属于”,二者必取其一且仅取其一 5.3.1 集合类型的定义 其定义格式如下:§type§<类型名称类型名称> = set of<基类型基类型>;;说明:(1)<基类型>表示集合中各元素的类型,可以是字符型、布尔型、枚举型和子界等顺序类型,不能是整型、实型和其他的构造类型3)若<基类型>为枚举类型或子界类型,则必须先定义该基类型,再定义集合类型4)一个集合最多只能有256 个元素另外,只有有序的类型才能跟关键字set of .5.3.2集合变量的声明 其声明格式与其他类型变量的声明完全相同 5.3.3变量集合的取值 集合变量不同其他变量,它不是一个单独元素,而是一系列元素的一集合集合变量的取值称为集合值,其一般表现形式如下:§[<元素元素1>,<元素元素2>,…,<元素元素n>] 如果集合类型的基类型有n个元素,则该集合类型变量的取值有2n个,包括一个空集合([ ])。
5.3.4集合类型的运算 集合类型的数据可以进行3大类运算:一类是集合对集合的并、交、差运算,其结果为集合值;一类是集合的关系运算, 其结果是 逻辑值;一类是元素对集合的“属于”运算,其结果也是逻辑值 运算名称运算名称 表示方表示方式式 运算结果运算结果 是否满足是否满足交换律交换律 并运算 S1+S2 两个集合中所有不重复元素组成的新集合 是交运算 S1*S2 两个集合所共有的元素组成的新集合 是差运算 S1-S2 所有属于S1但不属于S2的元素的集合 否相等运算 S1=S2 如果S1与S2所包含的元素完全相同,则结果为True,否则为False 是 不等运算 S1<>S2 如果S1与S2所包含的元素完全不同,则结果为True,否则为False 是 包含运算 S1>=S2 如果S2中的元素都在S1中,则结果为True,否则为False 否 被包含运算 S1<=S2 如果S1中的元素都在S2中,则结果为True,否则为False 否 属于运算 X in S1 如果元素X与集合S1的基类型相同,且被包含在S1中 ,则结果为True,否则为False. 否 集合运算符具有不同的优先级 ,如表所示 :5.4 数组与记录类型数组与记录类型 数组类型(Array)是一些具有相同类型的元素按一定顺序组成的序列。
数组中的每一个数据元素都可以通过数组名来存取,它们被顺序安排在内存中的一段连续的区域中Object Pascal提供的数组分为静态数组和动态数组而记录类型可以将不同 的数据集中 优先级 运算符 操作数类型 结果值类型 高 * 集合 集合 中 + 集合 集合 低 =、<>、>=、<= 集合 逻辑 最低 in 左操作数为元素,右操作数为集合 逻辑 在一起,并作为一个整体进行操作 5.4.1 静态数组 静态数组在程序初始化时必须分配内存单元,明确其固定的大小和元素的数据类型1.一维静态数组 数组通常分为为一维、二维和多维数组,定义一维静态数组类型的格式为:type<数组类型名数组类型名>=array[<下标类型下标类型>]of<基类型基类型>;; Object Pascal允许的下标的类型为整数类型、字符类型、布尔类型、子界类型、枚举类型等,而元素的类型可以 是任意的数据类 型,并且在同一数组中,所有元素的数据类型必须相同对于用户定义的数据类型作为下标类型,在使用之前必须声明 要访问数组中元素,可以用数组名加方括号,方括号内是元素的下标值 ,方括号内的下标值必须符合数组类型中下标类型的定义,其类型必须与下标类型一致,其值在下标取值范围内。
而且下标也可以是表达式 使用Object Pascal 提供的标准函数Low 和high ,可以返回一个数组的最小下标值和最大下标值,而函数Length可以返回数组的长度2.二维静态数组 二维数组是指一个一维数组中的元素类型又是一个一维数组,其一般形式为:type <数组标识符数组标识符>=Array[<下标类型下标类型1>]of Array[<下标类型下标类型2>]of <基类型基类型>;也可以把上述形式写成下面的形式:type<数组标识符数组标识符>=Array[<下标类型下标类型1>,<下标类型下标类型2>]of <基类型基类型>;3.多维静态数组多维静态数组的一般格式:type <数组标识符数组标识符>=Array[<下标类型下标类型1>,<下标类型下标类型2>,…<下标类型下标类型n>]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§<域名表域名表1>::<类型类型1>;;§<域名表域名表2>::<类型类型2>;;§...§<域名表域名表n>::<类型类型n>;;§end; 其中,<域名表>可以是多个合法的域名标识符,域名又称为字段名,<类型>可以是任意数据类型同一个记录类型中不能有同名的字段,而因为作用域的不同,记录内的字段名与记录外的标识符可以相同。
2.记录域的访问 由于记录类型中各字段的类型不同,所以不能同时访问记录的多个字段,而只能对记录的单个字段进行访问有两种方法:(1)记录变量名限定 为了标识记录字段所属的记录变量,使用记录变量名进行限定,格式如下:<记录变量名记录变量名>.<字段名字段名>则为记录的单个字段赋值可以使用如下语句:C1.Custid:=1;C2.IfPay:=True;(2)使用With语句 如果需要经常访问记录的字段,每次都用记录变量名进行限定非常麻烦,可以使用With语句加以简化With语句格式如下:With <记录变量名记录变量名> DO <语句语句> 其中,<语句>可以是简单语句,也可以是复合语句在<语句>中字段的访问不需要加记录变量名进行限定 3.记录的变体部分带有变体部分的记录类型的声明格式为:§type §<记录类型名记录类型名>=Record§<域名表域名表1>::<类型类型1>;;§<域名表域名表2>::<类型类型2>;;§...§<域名表域名表n>::<类型类型n>;;§Case <识别字段标识符识别字段标识符>::<识别字段类型识别字段类型> of§ <常量表常量表1>::<字段列表字段列表1>;;§<常量表常量表2>::<字段列表字段列表2>;;§…§<常量表常量表n>::<字段列表字段列表n>;;§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字符结尾的字符串的指针主要用于与外部函数如在Windows API中所用的函数兼容在Delphi 7中,可以把一个字符串直接赋值给一个Pchar类型的变量 5.5.5指针变量的动态使用1.New过程和Dispose过程 如果不使用@运算符为指针变量赋值,则指针变量称为动态指针变量,动态变量在访问之前必须首先分配内存单元Object Pascal提供了标准过程New,用来为动态变量分配内存单元,并把该单元的地址赋给指针变量,所分配单元的大小由指针所指的类型决定如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常调用New过程的格式如下: New(<指针变量名指针变量名>); 调用过程New(p)之后,可以用“p^”表示一个整型的动态变量,对其进行操作 当程序不再需要使用动态变量时,就调用标准过程Dispose删除New所创建的动态变量,并释放所分配的内存单元调用Dispose过程的格式如下: Dispose(<指针变量名指针变量名>);2.GetMem过程和FreeMem过程 标准过程GetMem用于为动态变量申请一块指定大小的内存区域,并把该区域的起始地址赋给指针变量。
如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常调用GetMem过程的格式如下: GetMem(<指针变量名指针变量名>,<区域大小区域大小>);; 如果程序不再需要使用动态变量时,就调用标准过程FreeMem删除GetMem创建的动态变量,并释放所分配的内存单元调用FreeMem过程的格式如下: FreeMem(<指针变量名指针变量名>);3.动态指针的应用举例 链表是一组元素的序列,在这个序列中,每个元素总是与它前面的元素相链接(第一个元素除外)这种链接关系可通过指针来实现 地址13地址212地址3 19地址4 7Nil图5-8 自然数链表 链表中的元素通常称为节点,第一个节点称为表头,最后一个节点称为表尾指向表头的指针称为头指针,在这个头指针中存放着表头的地址节点一般用记录来描述,描述节点的记录至少含有两个域,一个域用来存放数据,该域的类型根据要存放的数据类型而定,称为值域;另一个域用来存放下一个节点的地址,称为指针域表尾不指向任何节点,其指针的值为Nil 节点可以通过记录类型来描述,并且记录类型里包含一个指针域,链表节点的声明如下:§type§ Node=record§ data:string;§ next:^Node§end;§var§ Head:^Node; //定义头节点变量Head;或者采用如下方式:§type§ Link=^Node§Node=record§ data:string;§ next:^Node§end;§var§ Head:Link; 第第6章章 程序异常处理与调试技术程序异常处理与调试技术 在Delphi中有两种程序错误,一种是编译错误,在程序编辑阶段就可以由编译器发现并给出提示。
另外一种是运行错误,这类错误不能在编译阶段查出,只能在程序执行时发现,称为运行错误 Delphi提供了一种机制来处理运行错误,保护程序的正常执行,这种机制就是异常处理异常处理的方法是把正常的执行程序同错误的处理程序分离开来,这样可以保证在没有错误时,程序正常执行,当发生错误时,执行错误处理部分的程序,然后程序跳出保护模块,继续执行后续的程序 6.1 异常处理的目的异常处理的目的 为了避免程序在执行中出现可能超乎预料结果而导致错误的状况,然后对这些异常的状况做妥善的处理,不让异常的状况造成错误的结果致使程序异常停止,这就是异常处理存在的目的 可能会认为所开发的程序是可以执行的,因此不需要异常处理,然而当程序编译后没有错误发生时,并不表示程序就完美无缺,事实上某些异常状况是在执行中才发生的除此之外,有时程序还会受软、硬件环境的影响而发生程序异常的情况 异常处理,可以说是预防程序执行时发生异常而中断的一道防线,通过异常处理可以设法让程序避开异常的发生,不让它异常中断;或者在中断程序前,对数据做适当的处置,而不致丢失重要的数据6.2 Object Pascal异常的种类异常的种类 异常类的种类,主要可以分为两大类,一种是Delphi内建的异常类,另一种则是程序员自定义的异常类。
异常基类及其属性和主要方法 在Delphi中,所有异常的基类是Exception类所有其他异常类都是由该类派生而来1.exception属性 该类有两个基本属性:HelpContext和Message1)Exception.HelpContext属性该属性的定义如下:§Type ThelpContext= -MaxLongint..MaxLongint;;§Property HelpContext:ThelpContext; HelpContext是ThelpContext类的一个实例,它提供了与异常对象联系在一起的上下文相关帮助信息的序列号该序列号决定当发生异常时用户按F1键显示的一个异常错误的帮助信息 (2)Exception.Message属性该属性的定义如下:property Message: string 该属性存储异常发生时的错误信息可以通过该属性在提示错误对话框中显示错误信息字符串2.exception方法(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.1 Delphi内建的异常类 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】|【Debugger Options】|【General】选项,然后取消【Integrated debugging】选项,这样才能看到异常处理的效果6.4.1 Try…Finally…End结构只需要触发异常,程序系统将自动捕捉被触发的异常,然后以信息对话框显示出异常的信息,让程序避开发生异常的程序代码,然后向下执行程序 无论在“Try…Finaly”区内是否有异常被触发,都会接着执行“Findly…End”区的语句然而若是在“Try…Finally”区内有异常产生并被触发时,就会由异常发生点跳转此区域,转而执行“Finally…End”区的所有语句。
try try 没有异常没有异常 发生异常发生异常finallyfinallyendend运行界面图图6-3 Try…Finally…End执行方式Try…Fianally…end 的一般结构为:try语句语句 //预期可能产生异常的语句… finally语句语句 //无论是否发生异常都要执行的语句… end; 此结构中可以编写语句的区域有两个,而且其内语句使用目的并不相同 1.Try…Finally区中的语句 本区可包含多个语句,但这些是可能造成异常情况的语句而此处产生的异常, 包括由程序系统自动触发及程序员使用Raise 指令 触发的异常而无论使用Raise指令,还是由程序系统自动触发的异常,程序系统都会在其后“Finally…End”区执行完了时,自动捕捉被触发的异常,并且将异常信息显示出来2.Finally…End区中的语句 本区也可以有多个语句,但是不要在本区使用Raise指令,因为在上一区中由程序系统或raise指令触发的异常,其异常实体将存在本区,并在End关键字前显示异常信息。
倘若在本区使用Raise指令,则不管在“Try…Finally”是否有异常触发,都会执行Raise指令,并且显示异常信息,请勿在这个区区域使用Raise指令 6.4.2 try…except…end结构 使用try…except…end语法来处理异常时,可自行捕捉异常,然后根据异常的类型不同,对异常做不同的处理操怍 当“try…except”区内没有异常被触发时此区程序执行完之后,会跳过“execpt…end”区内的程序代码而离开“try…except…end”区域,直接执行其后的程序代码反之,若“try…except”区内有异常被触发,则在触发异常的情况下,就立即由异常产生点跳出“try…except”区,转而执行“except…end”区的程序 Try…Except…end 的一般结构为:try语句语句 //预期可能产生异常的语句… except语句语句 //捕捉异常的语句… [else 语句语句…] //可有可无的区域,一般语句(包括raise指令)end; 1.try…except”区中的语句 本区可以有多个语句,但这些是有可能造成异常情况的语句,而这里可能产生的异常, try try 没有异常没有异常 发生异常发生异常exceptexceptendend运行界面图图6-7 Try_except_End执行方式 包括由程序系统自动触发以及程序员使用raise指令去触发的异常,故在本区可根据状况条件来使用Raise指令。
然而在本区使用raise指令,或者由程序系统自动触发某些异常时,程序系统并不一定会自动处理这些异常,这时程序就有可能会异常中断,因此需要“Except…End”区中捕捉异常,并且对异常作适当处理;也可仿照“Try…Finally…End”语法,在“Except…End”区对“Try…Except”区内被触发的异常作再次触发(Reraise)的操作,即再次使用Raise指令,由程序系统自动捕捉异常,以信息对话框显示出异常信息,然后让程序避开异常,而不致于中断程序2.“Except…End”区中的语句 在“Except…End”区中,可以有多个语句,但此处主要是放置用来捕捉异常的语句,其目的是让程序仍自行捕捉异常,根据异常的类型决定要做的处理操作,而此种语句也有它特定的语法: On 异常对象标识符:类型异常对象标识符:类型 do //异常对象标识符可有可无语句;语句;//(on identifier:type do statement) 上述语法是表示当指定类型的异常被触发时,就执行保留字“do”后面这个语句反之若没有这种类型的异常被触发,则不会执行“do”后面的语句在捕捉异常的语句之后,还可以有一个“Else”区,在这个区域内可以有一般的语句(包括raise指令)。
若本区域内没有“Else”区域时,只要其内有捕捉异常的语句存在,就不允许有一般语句(包括raise指令);倘若本区内若有“Else”区,则除了“Else”区域之外,并不允许有一般语句存在于“Except…Else”区域,否则将导致编译错误6.5 程序调试程序调试 Delphi提供了一个功能强大的内置调试器(Integrated Debugger) ,该调试器可以方便地查找程序中出现的运行时间错误和逻辑错误所谓运行时间错误是指程序能正常编译但在运行时出错逻辑错误是指程序设计和实现上的错误 6.5.1调试的准备1.激活内置调试器 方法是:在Delphi集成开发环境中,选中【Tools】|【Debugger Options】|【General】页的【Integrated Debugging】复选框默认情况下该框被选中2.设置编译和调试选项 默认情况下,Delphi对有些错误和信息不给出调试信息可改变Delphi默认设置单击【Project】|【Options】|【Compiler】页 (1)Runtime Errors区域 Range checking:检查数组或是字符串的下标是否越界,默认时不检测。
I/O checking:检测输入输出错误,默认检测 Overflow checking:整型操作溢出检测,默认不检测选中该复选框调试器将对整数运算是否溢出做检测,默认下不报告错误2)Debugging区域 设置调试的信息默认时几乎全部选中一般无须改变该区域的选项设置 Debug information:表产生调试信息如果Debug Information 选中会在单元文件 (.dcu) 中放置调试信息,文件字节变大但不影响速度 Local symbols:产生局部变量的调试信息Local Symbols选中会添加与所在类、过程、函数及对象方法中定义的标识符等有关调试信息在程序调试时调试器会使用这些信息,但这些信息不会添加到可执行文件中除非在【Project】|【Options】|【Linker】页面中选中【Include TD32 Debug Info】选项,选中了此选项就可以使用TD32来调试 Reference info/Definitions only:用来产生 供Code Browser, Code Explorer and Project Browser使用的标识符引用信息。
如果Reference Info和Definitions Only 都被选中,则编译器将记录标识符定义位置信息如果仅选中了 Reference Info,表示编译器不仅记录标识符定义的位置,同时将记录标识符被引用的信息如果不选中Debug Information 和 Local Symbols 选项,仅选中该选项将不起作用Assertions:产生断言的调试代码Use Debug DCUs:使用连接的Dcu文件作为调试路径必须在【Tools】|【Debugger Options】|【General】页中指定调试文件的路径一般不选中该项 (3)Messages 区域 Show Hints:使编译器产生提示信息例如检测在过程或函数中声明了但一直没有使用的变量信息,或者无效的引用信息等 Show Warnings:使编译器产生警告信息3.编译程序发现编译错误 在调试之前,必须先编译通过可以选择【Project】|【Complie】 <工程名>可以对工程进行编译,检测编译错误也可以按【Ctrl+F9】执行同样的操作默认情况下,如果有错误或是警告和提示信息则显示在Message列表框中6.5.2 控制程序的执行 Delphi程序的调试命令都集中在RUN菜单下。
可以三种方式进行调试:【Step Over(F8)】单步执行调试、【Trace Into(F7)】跟踪调试或使用、【Run To Cursor(F4)】运行到光标所在处 Step Over一次执行一行语句,碰到调用过程时也是一步就执行过去,不会跟踪到过程的内部代码中去逐行执行,Trace Into则是在碰到过程或函数时跟踪到它们的内部,可以对其内部代码进行调试 Run To Cursor则从当前运行位置直接运行到光标所在的位置 如果光标所在的位置和当前运行位置处在不同的事件代码中,则不能直接运行到光标处,只有当发生了该事件才可以继续执行6.5.3 使用断点使用断点 断点(BreakPoint)就是使程序运行中断的点在一个应用程序总可以设置多处断点,当程序运行到断点处,会暂停执行,等待进一步的命令 1.断点的设置(1)单击选定代码行左边的空白2)在光标所在的行处按【F5】(3)使用【Run】|【Add Breadpoint】|【source breakpoint】 打开断点编辑对话框,在Line Number处输入需要加断点的行号即可 断点必须位于可执行代码行上,另外,断点既可以在设计状态下设置也可以在运行调试状态下设置。
一个有效(Enable)的断点默认的情况下该代码行显示为红色,正确的断点小圆点中是一个对号2.断点的删除和设置 删除一个断点,只要再次在已经设置为断点的代码行单击其左侧的空白处或按【F5】键就可以删除断点 如果一个应用程序许多位置都设置了断点,则可以使用断点列表框来管理所有的断点 使用【View】|【Debug】|【breakpoints】打开断点列表框,列表框将列出应用程序中设置的所有断点,无效(Disable)的断点前面的标志为灰色在列表窗口中单击右键,将显示一个断点设置快捷菜单,使用该快捷菜单可以实现对断点的添加、删除、使有效以及无效等操作1)利用断点列表窗口可以快速找到断点在源代码中的位置(2)断点功能的失效和恢复 在断点列表窗口单击右键,在快捷菜单中取消对Enable的选择或选择【breakpoints】|【Disable All BreakPoints】项可以使当前选中断点或所有断点失去功能 快捷菜单中的【Enable BreakPoint】和【Enable All BreakPoint】可以使相应断点恢复功能同样快捷菜单中的【Delete BreakPoint】和【Delete All BreakPoint】 可以删除当前选中断点或所有断点。
3.修改断点属性 在断点列表窗口选择断点后单击右键,在弹出的快捷菜单中选择Properties,则打开断点编辑对话框,用于显示和修改断点属性 也可以使用【Run】|【Add Breadpoint】|【source breakpoint】打开该对话框利用该对话框可以改变断点的位置,设置断点条件断点条件包括两种:布尔表示式和通过次数 Condition编辑框用于设置布尔表达式条件如果表达式值为真(或非零)则程序运行在断点处中止;否则调试器将忽略该断点 Pass Count编辑框用于设置通过次数条件,即只有当程序运行在该断点处通过设定次数时程序运行才在该断点处中止 同时设置时,Pass Count是指满足条件的通过次数6.5.4 监视数据的值监视数据的值 1.监视表达式 选择【View】|【Debug Windows】|【Watches】可以打开监视列表窗口Watch List在该窗口中单击鼠标右键,在弹出的快捷菜单中选择Add Watch打开监视属性对话框,可以添加新的变量或表达式也可以使用【Run】|【Add Watch】打开监视属性对话框 在Expression右边的编辑框中添加要监测的变量或表达式,同时设置其属性。
当该表达式代表一个数据元素时,可以在Repeat count中指定其重复次数如果要监测的是一个数组的值,可以使用Repeat count指定数组元素的下标 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按钮观察表达式的当前值而后在New Value编辑框中输入或选中一个新值,单击Modify按钮确认并更新数据项但一般不要修改指针和数组下标 单击Watch按钮可以打开监视列表窗口,并添加选定的表达式到列表窗口中 单击Inspect 按钮可以为选择的数据元素打开一个信息的窗口,这对于观测数据结构、类和数组的值特别有用 3.函数调用 选择【View】|【Debug Windows】|【Call Stack】可以显示调栈窗口(Call Stack Window)调栈窗口的顶端列出了应用程序最近的函数调用利用调栈窗口可以退出当前跟踪的函数,可以利用快捷菜单项显示或编辑位于特定函数调用处的源代码4.观测局部变量 当调试的程序位于某一个过程或函数内部时,可以选择View|Debug Windows|local variables可以打开局部变量显示窗口该窗口显示在该过程或函数内部使用的所有局部变量及其值 第第7章章 键盘、鼠标和文件编程键盘、鼠标和文件编程7.1键盘的编程键盘的编程7.1.1关于键盘 在计算机发展过程中,一开始是就使用键盘作为输入方式,在DOS环境中,大多只有判断键盘是否单击,以及单击的是哪个键,在Windows之中,可以判断键盘单击的事件有三种如下图所示: 在这些事件中,OnKeyDown和OnKeyUp都会传入用户单击的Key值(word值),可以利用这些值,来判断用户按了哪些键,而这些值代表了Windows中的Virtual Key Code。
而OnKeyPress所返回的是一个Char值,代表一个ASCII字符 ASCII字符和Virtual Key Code是不相同的,因为Virtual Key Code中有代表ASCII的字符,但ASCII字符不包含全部的Virtual Key Code,因为Virtual Key Code中内含了很多功能键 7.1.2 键盘常用事件1.OnKeyDown 当按下键盘上的任一个键时,就会触发此事件如字母键、数字键、功能键(F1~F12)、Ctrl键、Shift键或Alt键等,都将产生一个OnKeyDown事件2.OnKeyPress 当用户单击ASCII字符的键盘时,就是说当按下键盘上的一个字符键,如字母键、数字键等会产生一个OnKeyPress事件,但是单独按下功能键(F1~F12)、Ctrl键、Shift键或Alt键等,不会产生OnKeyPress事件3.OnKeyUp 当按下键盘上的任一个键后松开时,都会产生一个OnKeyUp事件对于功能键(F1~F12)、Ctrl键、Shift键或Alt键等,也会产生一个OnKeyUp事件 4.检测功能键 在组件的OnKeyDown、OnKeyUp、OnMouseDown和OnMouseUp等事件的处理过程中,有一个TShiftState类型的变量Shift,TShiftState类型定义如下:Type TShiftState=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会同时触发OnMouseDown OnClick OnMouseUp OnMouseMove当用户在对象A单击但在对象B放开时,A会触发OnMouseDown OnMouseMove OnMouseUp B会触发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;否则,将其设置为False3)处理拖曳操作 在第二个组件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,否则为FalseReset过程的声明如下: procedure Reset(var F[:File;RecSize:Word]); 其中F是一个任意文件类型的变量,RecSize(记录长度)是一个可以默认的表达式,并且仅当F是一个无类型文件时才用,该参数指定数据传送时的文件大小。
2)以写方式打开文件(Rewrite) 通过调用Rewrite函数可创建并打开一个新文件如果F是一个文本文件,那么文件变量(F)的属性为只读如果文件已经打开,则先关闭然后在重新创建, 当前文件的位置设 置在文件开始调用Rewrite后,则Eof(F)必为TrueReset过程的声明如下: procedure Rewrite(var F:File[;Recsize:Word]); 其中F是一个任意文件类型的变量,RecSize(记录长度)是一个可以默认的表达式,并且仅当F是一个无类型文件时才用,该参数指定数据传送时的文件大小3)用Erase过程删除文件 通过调用Erase过程可删除一外部文件Erase过程的声明如下:procedure Erase(var F); 其中F是一个任意文件类型的变量,调用Erase过程将删除同F相连的文本文件 3.文件的基本操作函函 数数 与与 过过 程程 名名 实实 现现 功功 能能 functon IOResult:Integer; 返回最近一次I/O操作的状态值 procedure Rename(var F;Newname:string);procedure Rename(var F;Newname:PChar); 用Newname重新命名文件 procedure ChDir(S:string); 将当前目录改为字符串S制定的目录 function Eof(var F):Boolean;Text files;function Eof[(var F:text)]:Boolean; 检测文件是否结束,如果结束和文件为空则为Ture,否则为False procedure GetDir(D:Byte;var S:string); 检测由D指定驱动器的当前目录。
D的取值可为(0,1,2,3),其分别代表(默认,A,B,C) procedure RmDir(S:string); 删除由字符串S指定的目录 procedure MkDir(S:string); 新建一个由字符串S指定的目录,改目录原来并不存在 7.3.2 适合于文本文件的基本操作1.以添加方式打开文件(Append) 通过调用函数Append可打开一个已经存在的文件以便于在文件末尾添加文本如果在文件最后的128个字节块中,存在字符
通过调用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.文件的基本操作函函 数数 与与 过过 程程 名名 实实 现现 功功 能能 Procedure AssignPrn(var F:Text); 建立文本文件变量同打印机之间的联系 Function Eoln[(var F:Text)]:Boolean; 检测文件指针是否指向行尾 Procedure Flush(var F:Text); 输入方式(Rewrite或Append)打开的文件缓冲区,以确保所有写入文件的字符都被写入外部文件 Function SeekEof[(var F:Text)]:Boolean; 返回文件尾状态 Function SeekEonln[(var F:Text)]:Boolean; 返回文件行尾状态 Procedure SetTextBuf(varF:Text;varBuf[;Size:Integer]); 设置文件缓冲区 7.3.3有类型文件 有类型文件是一种具有一定数据类型的文件,它是由指定数据组成,读写过程所操作对象的单位是一个指定类型的数据。
由类型文件的变量可声明如下: Type fileTypeName=file of type 其中file of 为保留字,type可以是各种数据类型如整型、实型及记录型FileTypeName为类型文件名1.有类型文件的读取和写入方法 对于有类型文件允许同时为读和写打开通过调用Read过程可以从文件中读取数据 其中F,及V1,V2,…,Vn的定义同文本文件其声明如下: procedure Read(F,,V1[,,V2,,…,,Vn]); 通过调用Write过程可以向文件中写入数据其声明如下: procedure Write(F,,V1,,…,,Vn);2.文件的基本操作函函 数数 与与 过过 程程 名名 实实 现现 功功 能能 Function FilePos(var F):Longint; 返回文件的当前文件指针位置 Function FileSize(var F):Integer; 返回文件的大小 Procedure Seek(var F;N:Longint); 将文件指针移至文件位置 Procedure Truncate(var F); 截去当前位置后的所有数据 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中,图形图像的产生有4种方式:§(1)在程序执行时由程序绘制;§(2)设计期间使用Shape组件给出;§(3)执行期间由用户自己制作;§(4)直接读取已存在的图形图像文件 一般来说,最常用的绘图方式是各种几何图形的绘制,如直线、圆和椭圆、矩形等8.1.1图形图像对象组件与图像种类1.图形图像对象组件(1)画布对象(TCanvas):TCanvas是许多组件都具备的一个属性同时它本身也是一个对象,包含自己的属性,其中最重要的有4个:画笔、画刷、字体组件,以及图形像素数组TCanvas对象提供了作图操作的平面及各种工具,使用这些工具在这个平面上绘制各种线条、曲线以及其他形状2)图形对象(TGraphics):TGraphics对象是图像文件在内存中的抽象代表,用于存储图像文件,以便将其从磁盘装入内存,或从内存存放到磁盘 TGraphics有3个派生类: 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(graph display interface,图形显示界面)例程的文件,允许将对GDI函数的调用保存到外存同时,可与其他程序共享作图例程图元文件可以平滑地改变大小(位图在放大后会失真)图元文件有两种格式:标准图元文件(.wmf)和增强图元文件(.emf)Delphi TMetaFile支持这两种图元文件4)JPeg图:Jpeg文件扩展名.JPG Jpeg是 一种静态图形压缩算法,图像质量可以调节,压缩比率较高这种文件的读写以及和位图的转换都要经过压缩或者解压在Delphi7中,如果要操作Jpeg文件,需要在单元中包含Jpeg单元名8.1.2 图像组件(Image) Image组件是一种图像的容器,用于显示各种以文件形式存储磁盘上的位图、图标、图元文件或用户自定义的图形文件。
设计阶段指定图片的方法是:单击对象编辑器的Picture属性行的右格中的 按钮,打开图片对话框,然后选择一幅图片 在应用程序运行期间,可以调用相关的函数或过程动态地从文件中载入图形图像 Image组件常用属性 属性 类型 作用 Picture Tpicture 指定图片,可为位图(BMP)、图标(ICO)或图元(EMF、WMF)文件,若为Jpeg图片,当前单元Uses语句中应包含Jpeg单元 Canvas Tcanvas 提供图像组件进行绘图操作的平面 Autosize Boolean 其值为True时,Image自动调节大小以适应图像默认为True Streth Boolean 其值为True时,图像自动调节大小以适应Image,但.ICO文件不能默认为False Transparent Boolean 默认值为False确定是否允许图像组件下面的物体显示出来 Center Boolean 其值为True时,图像居中,否则,从左上角开始显示默认值为False 8.1.3图形组件(Shape) Shape组件用于在窗体上绘制一些常见的几何图形,如矩形、圆和圆角矩形等。
作图时常用的属性有Shape、Brush和Pen等1.Shape属性 Shape组件的Shape属性用于指定要绘制的几何图形种类,属于TShapeType类型该属性可能的取值有:stCircle(图)、stEllipse(椭圆)、stRectangle(矩形)、stRoundRect(圆角矩形)、stRoundSquare(圆角正方形)和stSquare(正方形) 在设计期间,可以通过鼠标拖放改变图形的大小,在运行期间,可以通过Height和Width属性改变图形大小2.Brush属性 Brush(画刷)属性指定图形填充的模式和颜色在对象编辑器中,Brush属性栏中有“ ”符号,展开后可看到子属性Color和Style1)Color子属性:包含一系列预定义的颜色,用作几何图形的填充色2)Style子属性:确定几何图形的填充样式,可取8种不同的值 3.Pen属性 Pen(画笔)属性指定线型、线宽和线的颜色它也像Brush属性一样包含子属性, 它的子属性是Color、Mode、Style和Width其中最常用的是Style和Width1)Style子属性:确定线型子属性的取值有psSolid、psDash、psDot、psDashDot、psDashDotDot、psClear和psInsideFrame,分别表示实线、破折号、圆点等。
2)Width子属性:表示线宽,默认值为18.2画布对象画布对象 画布对象Tcanvas本身也是一个对象(组件),包含绘图中使用的各种方法和属性,最重要的有画笔、画刷、字体组件以及图形像素数组4个组件,但一般不能单独使用8.2.1像素操作 像素是构成图形最基本的单位画布上的每个点都有一个对应的像素,用来代表图形上某点的颜色一般情况下,并不需要直接存取像素,而是调用画笔和画刷这样的处理像素的工具 直接读取像素也很简单:画布上的一幅画对应一个存储像素的矩阵(二维数组),矩阵中的一个元素代表一个点的颜色可以读取一个像素或者设置它的颜色 下面语句的功能是:读取一个像素,并将它设置为红色 Canvas.Pexels[X,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属性确定画笔与屏幕上原有点的混合方式可选的值有pmCopy Copy显示模式默认值pmNotXor使用背景显示模式2.画线的方法(1)Moveto方法,作用是将画笔移到指定位置,使用方法为:moveto(x,y,integer)2) lineto方法,作用是画一条到指定位置的直线段,线段起始位置由画布对象的Penpos属性值即画笔的当前位置确定使用方法为:lineto(x,y: Integer)。
3)画折线的方法,使用方法为Polyline(points:array of TPoint) 其中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属性提供对Windows GDI对象句柄的访问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.4 PanitBox画框组件 PanitBox在窗体中提供一个可以用来绘制几何图形的矩形区域,可以使用绘图语句,在这个区域内绘制各种图形PanitBox组件在system页中,双击则可在窗体中添加8.3音频和视频播放音频和视频播放8.3.1音频播放 Delphi环境的MediaPlayer组件封装了WindowsMCI(multimedia control interface,多媒体控制接口)的大量函数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(Windows 95以上版本)提供的具有媒体播放能力的窗口控件,可连续播放无声的AVI剪辑文件 AVI文件格式是微软公司标准的音频和视频文件存储格式在AVI文件中,音频和视频交织存储,每帧都有音频和视频,且实时、直接地送往音频和视频硬件缺陷是难以编辑和适应扩充了的多媒体“展播” Delphi环境的卡通控件Animate封装了Windows的卡通控件,它的属性和方法如下:1.Animate组件的主要属性(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剪辑文件,方法原型为 Procedure Play(FromFram,ToFram:Word;Count:Integer); 其中,FromFrame指定起始帧,ToFrame指定终止帧,Count指定播放次数。
2)Reset方法:使组件复位为默认值方法原型为:Procedure Rset;(3)Seek方法:播放所指定的帧方法原型为: Procedure Seek(frame:Fmallint); 其中,Frame参数用于设置播放的帧序号4)Stop方法:终止播放操作方法原型为: Procedure Stop;8.3.3 媒体播放器控件 媒体播放器控件是一个具有多媒体播放能力的窗口控件它通过Windows操作系统的MCI接口直接控制各种类型媒体播放设备 按扭从左到右分别为:Play(播放)、Pause(暂停播放和录制)、Stop(停止播放和录制)、Next(跳到下一磁道,媒介不支持磁道时,跳到最后)、Prev(跳到前一磁道,媒介不支持磁道时,跳到最前)、Step(前进若干帧)、Back(返回若干帧)、Record(开始录制)、Eject(释放媒介)1.媒体播放器的属性(1)Device属性图8-10媒体播放器组件的形式 TMPDeviceTypes类型指定进行播放的设备类型 DeviceType属性的值 设备类型 播放的媒体文件类型 dtAVIVideo AVI视频文件 dtCDAudio CD唱盘 dtDAT 数字音频磁带 dtDigitalVideo AVI、MPG、MOV文件 dtMMMovie MM电影 dtOverlay 模拟视频 dtScanner 图像扫描设备 dtSequencer MIDI文件 dtVCR WAV文件 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】在弹出的【New Items】对话框中,选择DLL Wizard图标,单击【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子句中,就可以实现输出了1.Exports关键字的使用 用关键字Exports引出函数或过程,表明编译时要使用远程地址调用,使得函数或过程在DLL内可被其他模块访问 Exports有如下几种形式:(1)exports例程名; //名字引出(2)exports例程名 index索引值; //索引值引出 index用来指示为一个函数或过程分配一个顺序号,其值的范围是为1~327673)exports例程名 name 别名; //别名引出 name后面接的是字符串常量,用来指出该过程或函数的输出名 (4)exports例程名name别名resident //resident选项9.1.3 DLL文件的静态调用 DLL文件的调用一般有两种方法,即静态调用方法和动态调用方法,静态调用方式,就是在本单元的Interface部分用External指示字列出要从DLL中调用的过程;动态调用方式是通过调用Windows API中的LoadLibrary函数、GetProcAdrress函数和FreeLibrary函数来实现DLL文件的动态调用。
1.静态调用 静态调用又称为隐式加载方式,它是通过单元体中Interface部分的External指示字所列 出需要调用DLL文件的过程或函数这些被指定的DLL文件中的过程是在程序执行之前被加载进内存的 静态调用方式在使用DIL中的函数或过程之前,先引入DLL中的函数或过程引入DLL中的例程一般有三种方法:(1)通过过程名或函数名来调用: function funcName(参数):DataType;tdcall;external ‘DLL文件名’ (2)通过过程或函数的别名来调用: function funcName(参数):DataType;stdcall;external 'DLL文件名' name '别名‘(3)通过过程或函数的索引来调用:function funcName(参数):DataType;stdcall;external 'DLL文件名' index n 在Delphi7中,存在一个调用约定的问题调用约定就是指调用例程时参数的传递顺序,在Delphi中DLL支持的调用约定如表所示 使用静态调用方式,有两个缺点:一是当要加载的DLL文件不存在或者没有指定调用的过程时,程序就会自动停止运行,或可能致使程序出错;二是一旦所需的DLL文件被加载至内存后,即使不再需要,其仍然停留在应用程序的地址空间中。
调用约定调用约定 参数传递顺序参数传递顺序 Register 从左到右 pascal 从左到右 Stdcall 从右到左 Cdecl 从右到左 Safecall 从右到左 9.1.3 DLL文件的动态调用1.动态调用 动态调用又称为显示加载方式,它可以解决静态调用中存在的局限性动态调用不需要在单元体Interface部分中把需要调用的所有DLL过程都列出,其只要在调用前引用,并且使用LoadLibrary函数指定需要加载的DLL文件,使用GetProcAddress函数指定所调用的过程或函数,并返回该过程或函数的入口地址,使用FreeLibrary函数实现将该函数从内存中移除此外,如果指定的DLL文件出错,不会导致程序终止运行 动态调用DLL主要用到三个Win32 API函数:LoadLibrary()、FreeLibrary()、GetProcAddress() (1) Loadlibrary(): 把指定库模块装入内存其使用的语法为: function Loadlibrary(LibFileName: PChar): THandle; LibFileName指定了要装载DLL的文件名,如果LibFileName没有包含一个路径,则Windows按下述顺序进行查找:当前目录;Windows目录;Windows系统目录;包含当前任务可执行文件的目录。
列在PATH环境变量中的目录以及网络的映象目录列表 如果函数执行成功,则返回装载库模块的实例句柄 (2) GetProcAddress:取得给定模块中函数的地址语法为: function GetProcAddress(Module: THandle; ProcName: PChar): TFarProc; Module包含被调用的函数库模块的句柄,这个值由Loadlibrary返回如果把Module设置为nil,则表示要引用当前模块3)Freelibrary():从内存中移出库模块语法为: procedure Freelibrary(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控制向导 在【VCL Class Name】框内选择组件模板上的一个可视组件,然后在【New ActiveX Name】框内输入要创建的ActiveX控件名,通常最后一个字母是大写的X也可以接受缺省的控件名 在【VCL Class Name】列表框中并没有包还所有的VCL控件,只有符合下面三个条件的VCL控件才会被列出来一是该VCL控件必须已经安装到了组件面板上;二是该控件必须继承于TwinControl;最后一个条件是该控件没有调用过RegiserNonActiveX() 在【Implementation Unit】中显示ActiveX实现的单元名称在【Threading Model】中显示用于选择线程的模式,可以选择Apartment,free,single或both3)单击OK按钮Delphi将创建下面的文件:①ActiveX项目文件:ButtonXControl1.DPR②类型库文件:ButtonXControl1.TLB③类型库的接口源文件:ButtonXControl1_TLB.pas④ActiveX接口实现单元文件:ButtonImpl1.PAS9.2.2添加新属性 要给接口增加属性或方法,需要用【View】|【Type Library】|【Type Library】编辑器,同时在IButtonX上单击鼠标右键,选择 【New】|【Property】。
单击该菜单项后,将弹出如图所示的对话框 图9-7 添加新属性示意图 添加属性Mycaption,其Type(数据类型)选择BSTR可以看到在左侧的树型列表框中增加了两个Mycaption通过查看ButtonXControl1_TLB.pas文件, 可以在IbuttonX接口的声明中看到这样的声明: property Mycaption: WideString read Get_Mycaption write Set_Mycaption; 其中Get_Mycaption和Set_Mycaption是属性Mycaption的访问方法 选择【Project】| 【Build ButtonXControl1】命令,就可以生成ButtonXControl1.ocx文件此时可选择【Run】| 【Register ActiveX Server】将它注册到系统注册表中 注册以后,就可以在【Import ActiveX Control】对话框中的组件表中看到它了该对话框可以通过【component】|【import activex control】打开 ,如果没有先注册【ActiveX】控制,则在组件列表中看不到它,可以单击【Add..】按钮将其加进来 。
9.2.3 ActiveX的使用 在下列两种情况下考虑使用ActiveX控件:1.没有合适的delphi组件可以使用时2.想使用多种编程语言进行开发,想在多个开发平台间共享一些控件时 把ActiveX控件安装到delphi组件面板的步骤如下:1.点击菜单【component】|【import activex control】 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.关系数据库(Relational DataBase) 关系数据库是由若干个表组成的,每一张二维表对应着一种联系表的每一行称为记录;表的每一列叫做字段;域就是属性的取值范围对于dBASE、FoxPro、Paradox这三种数据库系统,数据库对应于某一个子目录,而其他类型如MS Access、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.1 SQL语言的发展 目前SQL语言被广泛地使用,它具有强大的生命力,它使用所有数据库用户包括程序员、DBA管理员和终端用户都受益非浅。
SQL语言具有以下优点:(1)SQL语言是所有关系数据库的公共语言2)SQL是非过程化查询语言 有两种方式使用SQL语言:一种是在终端交互方式下使用,称为交互式SQL语言;另一种是嵌入在高级语言编写的程序中使用,称为嵌入式SQL语言 10.2.2 SQL的基本查询功能 SELECT语句是使用最多的SQL语句,它完成的是数据库的查询功能,SQL SELECT语句,从数据表中选择出符合条件的记录 Distinct语句的作用是对某个表所选择的字段数据,忽略重复的情况,也就是说,针对某个字段查询出来的记录结果是惟一的 1.TOP和ORDER BY语句 TOP语句实现从第一条或最后一条开始(利用ORDER BY条件子句),返回特定记录的数据的功能如果没有加上ORDER BY条件的话,所得到的数据,将会是随机的数据此外,在TOP语句之后,除了可以加上 数字以外,还可以利用保留字PERCENT来查询排序参数:ASC递增顺序排列,默认值DESC递减顺序控制2.IN条件字句 指定要查询哪一个外部数据库的表3.HAVING条件子句 指定一特定的分组记录,并满足HAVING所指定的条件或状态,但条件是针对分组的条件设置。
4.GROUP BY条件子句 依据指定的字段,将具有相同数值的记录合并成一条 5.BETWEED…AND运算符 决定某一数值是否介于特定的范围之内 6.LIKE操作数 将一字符串与另一特定字符串样式比较,并将符合该字符串样式的记录过滤出来10.2.3 SQL的其他应用1.SQL数字函数(1)AVG:算数平均数 (2)COUNT:计算记录条数 (3)FIRST与LAST:返回某字段的第一条数据与最后一条数据(4)MAX与MIN:返回某字段最大值与最小值5)SUM:返回某特定字段或是运算的总和数值2.SQL查询的嵌套 嵌套的SQL查询含义在于:在一个SQL语句中可以包含另一个SQL查询语句,形成内部嵌套的查询类型,SELECT语句构成的多层SQL查询,必须用()将该语句括起来 3.SQL与数据库的维护(1)表的建立CREATE TABLE语句:我们可以利用这个命令,来建立一个全新的表,但前提则是数据库必须已经存在 (2)表索引的建立CREATE INDEX语句:这个命令主要是对一个已经存在的表建立索引 3)表的删除DELETE语句:我们可以利用DELETE语句,将表中的记录删除。
(4)SELECT…INTO语句:可以通过这个命令,利用已经存在的表查询,来建立一个新表5)INNER JOIN 操作数:当某一个共同的字段数据相等时,将两个表的记录加以组合 (6)UNION操作数:可以通过UNION操作数来建立连接的查询条件,UNION操作数可以将两个以上的表或是查询的结果组合起来 (7)ALTER语句:在一个表建立后,利用ALTER语句,可以去修改表的字段设计 (8)DROP语句:针对所指定的表或字段加以删除,或是把索引删除 (9)INSERT INTO语句:新建一条数据到表当中 (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.1 BDE简介 Delphi的Borland数据引擎(BDE)可直接访问某些本地数据库,包括dBASE,Paradox,FoxPro,Microsoft Access,Delphi安装程序自动为这些数据库安装了驱动程序,并建立了相应的配置如果将BDE与Borland的SQL Link驱动程序连接,可以访问Oracle,Sybase,Informix,InterBase和IBM DB2数据库 如果要连接的是BDE所不支持的数据库,则可利用BDE所提供的ODBC管道,通过ODBC来访问它们10.4.2配置BDE数据源1.BDE管理器 BDE管理器使管理Windows注册表中的BDE系统配置信息和BDE配置文件(IDAPI.cfg)中的别名信息的可视化工具选择菜单项【开始】|【程序】|【Borland Delphi7】|【BDE Administrator】,即打开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)在Databse Driver Name下拉列表中选择一种数据库驱动程序,单击OK按钮,生成一个数据源如果选择的是STANDARD,则自动生成名为Standard1的数据源,并显示在Databases页的树形数据库别名表中3)根据需要修改数据源的名称,配置右侧Definition页中的参数值 4)右击刚创建的数据库别名,选择快捷菜单Apply项,按提示保存数据库别名配置10.4.3 建立ODBC数据源 以Access数据库为例说明创建ODBC数据源的方法1.打开ODBC数据源管理器在BDE管理器窗口中,选择Object菜单的ODBC Administrator项,或使用Windows控制面板中的ODBC对象,打开ODBC数据源管理器窗口 为了便于访问数据,Windows系统提供了ODBC数据源管理工具,该工具用来设置数据源的名字DSN(Data Source Name)DSN是一个数据源的标志,目的是便于应用程序访问数据,即只要某个数据设置了相应的DSN,应用程序就不必理会该数据库存储的位置和驱动程序,可以按DSN直接访问数据库。
DSN有三种类型:(1)用户DSN :只对设置它的用户可见,而且只能在设置了该DSN的机器上使用2)系统DSN:对机器上的所有用户都是可见的,包括NT服务3)文件DSN :将DSN的配置信息存在一个文件里,这样的文件就叫文件DSN2.选择【用户DSN】选项卡,单击【添加】按钮,打开创建数据源对话框选择Microsoft Access Driver(*.mdb),并单击【完成】按钮3.在弹出的对话框中设置数据源名为Huoyun,通过【选择】按钮选择数据库,或通过【创建】按钮创建新数据库,还可以修复和压缩数据库,还可通过“高级”按钮设置登录名称和密码点【选择】按钮,选择一个已经存在的Access数据库D:\My Documents\HY.mdbs数据库4.输入数据源的名称“huoyun”,说明部分输入一些对所创建的数据源进行描述的文字,也可不输入,设置之后,单击【确定】按钮保存设置这样就配置好了DSN,关闭ODBC数据源管理器 关闭所有使用BDE的应用程序,打开BDE管理器窗口,切换到DataBase页,即可看到新建的ODBC数据源“huoyun”10.4.4 使用数据库浏览器 Database Explorer的功能是定义数据库,装载数据库,管理数据库,还可以直接执行SQL语句。
1.打开数据库浏览器窗口 选择菜单项【开始】|【程序】|【Borland Delphi 7】|【SQL Explorer】,或在Delphi集成开发环境的主窗口中选择菜单项【Database】|【Explore】,都可以打开SQL Explore窗口 在SQL Explorer窗口的Databases页,左(Databases)窗格显示所有的数据库别名列表,右(Definition)窗格显示当前数据库别名的配置本例中,需要给数据库浏览器指定要操作Microsoft Access示例数据库HY.mdb方法如下:(1)在左窗格选定已建立的数据库别名HYDB2)在右窗格修改HYDB的配置,利用DATABASE NAME格的按钮,将该属性设置为d:\My Docments\HY.mdbB 3) 在右窗格修改ODBC DSN的配置,选择下拉框Huoyun选项4)选择Object菜单Apply项,保存所做设置 2.打开数据集 在数据库浏览器中打开数据集步骤如下:(1)在SQL Explorer窗口Databases页左窗格中,选定数据库别名HYDB2)选择Object菜单的Open项,弹出数据库连接(Database Login)对话框。
单击OK按钮,即可打开HY.mdb数据库3)单击HYDB项左侧的 号,展开它,则HY.mdb数据库中的数据库表对象、查询对象都会显示出来 4)选定“报价”表,并在右侧切换到Data窗格,“报价”表内容在Data窗格显示出来3.操纵数据库(1)浏览数据●选定整个记录:数据表最左边有一列用于选定记录的按钮,单击某个即选定它所在的记录;可利用垂直滚动条配合操作另外,工具栏右半部是与DBNavigator控件相仿的如下图所示查找工具栏,可用于移动记录指针或进行插入、删除等操作●选定整个字段:单击字段名即可选定一个字段,可利用水平滚动条配合操作●选定指定记录的指定字段:数据集中的数据是以网格形式显示的,单击某个网格,即可选定它可利用垂直滚动条和水平滚动条配合操作●浏览备注型字段(以Memo标记):备注型字段内容是一些长度差别较大的字符串单击某个记录中备注型字段的网格时,会弹出一个窗口,显示它的内容(文字或空白)●浏览图片字段(以Graphics标记):单击图片字段所在的网格,就会弹出一个窗口,显示该字段的内容(图片或空白)2)利用数据库浏览器也可对数据集进行编辑、插入、删除、复制和移动等操作。
●编辑操作:选定要编辑记录,单击Edit Record()按钮切换到编辑状态,然后修改各字段的值编辑结束后,单击Cancel Edit()按钮或选定其他记录退出编辑状态●插入操作:选定一个记录,单击Insert Record()按钮,则所选记录之前插入一个空记录(带有*符号)输入一个记录的内容之后,单击Post Edit再插入一个记录,或单击Cancel Edit退出插入状态●删除记录:选定要删除的记录,单击Delete Record()按钮,则弹出一个消息框提问是否真要删除单击OK按钮即可删除记录,单击Cancel按钮不删除记录4.使用SQL语句执行查询使用SQL查询数据库的步骤如下:(1)在左窗格中选择一个数据库对象(表、查询等) (2)在右窗格中,切换到Enter SQL页,在编辑框中输入一个SQL语句 (3)单击execute query按钮,执行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)右击字段编辑器,选择ADD Fields项,弹出添加字段对话框3)在添加字段对话框中选定要创建静态字段对象的字段,单击OK按钮,将选定的字段添加到字段编辑器中 。
4.访问静态字段 选择要设置属性的字段,在对象观察器中修改字段对象的属性1.Alignment :设置字段在数据控制组件中的对齐方式,左对齐、右对齐和居中2.Display Label:说明字段对象咱DBGrid组件中的显示的标题,默认为字段对象名称3.Display Width:字段对象在DBGrid组件中显示的列的宽度4.FileName:字段对象对应的字段名称5.Index:字段对象在DataSet组件中的索引号,从0开始6.Name:字段对象名7.ReadOnly:相应字段是否能修改 8.Visible:相应字段是否在DBGrid中显示9.EditMask:设置掩码编辑格式10.MaxValue和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属性编写语句和某个数据集相连并相连并选择要显示的域2.SQL命令文本的编写(1)使用字符串列表编辑器编写 利用Query组件的SQL属性打开字符串列表编辑器,键入SQL语句之后,单击OK按钮可将编辑器中的SQL命令文本装入SQL属性中也可右击编辑器,选择Save项,将编好的SQL命令保存到一个文件中供使用 打开字符串列表编辑器之后,还可右击编辑器,选择快捷菜单的Load项,从一个SQL命令文件中调入SQL命令2)使用SQL构造器(SQL Builder)可用于编写Select语句。
右击Query组件,选择快捷菜单的SQL Builder项,弹出SQL Builder对话框 SQL构造器的上半部用于选择数据、数据表,以及表中的字段等;下半部用于选择性地构造查询条件在SQL构造器中组织好一个查询并退出构造器时,其中的SQL命令会自动写入相应SQL Builder组件的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语句中的参数,即命令中第一个参数对应Params[0],第二个参数对应Params[1],依此类推 (2)使用ParemByName函数按名称访问参数 ParemByName是一个函数,用动态SQL语句中的参数作为调用ParemByName函数的参数,这样便可为其赋值 (3)使用DataSource属性从另一个数据集获得参数 使用上述两种方法的前提是用户预先知道具体的参数值,而在有些程序中,参数值是无法确定的这就需要设置Query组件的DataSource属性值,其值为另一个DataSource组件的名字 10.6 基于基于ADO的数据库应用程序的数据库应用程序10.6.1 ADO组件 ADO组件(Delphi中称为dbGo组件)位于Delphi组件面板上的ADO页如图所示,这些组件可用于连接ADO数据存储、执行命令、检索基于ADO机制的数据库表中的数据。
多数ADO组件都能在按其功能在BDE组件面板中找到相应的组件 图10-24 ADO组件页1.ADOConnection ADOConnection组件位于ADO组件板的第一个组件,该组件用于与数据库建立连接TADOConnection组件中还有两个属性:CommandTimeout和ConnectionTimeout这两个属性主要用于连接远程数据库时相关参数的设定 ADOConnection组件和BDE组件板中的Database组件功能相似 2.ADOCommand ADOCommand组件位于ADO组件板的第二个组件,该组件有CommandType与CommandText两个重要属性 3.ADODataSet ADODataSet组件位于ADO组件板的第三个组件,通过ADODataset组件,可以直接与一个表进行连接,也可以执行SQL语句,还可以执行存储过程 在使用时,首先设定其Connection属性为ADOConnection组件,没有ADOConnection组件就直接设定ConnectionString属性接着有两个重要属性CommandType与CommandText需要设定 。
4.ADOQuery ADOQuery组件位于ADO组件板的第四个组件,提供数据库连接接口一致化 5.ADOTable ADOTable组件位于ADO组件板的第五个组件,如果程序中使用ADOConnection连接组件,直接设定该组件的Connection属性为ADOConnection组件即可另外一个重要属性就是Active属性,该属性用来设置打开或关闭与该组件相连数据表其值若为True,则打开数据表;若为False,则关闭数据表 ADOTable组件与BDE中的Table组件对应6.ADOStoredProc ADOStoredProc组件位于ADO组件板的第六个组件,用于完成数据存储过程 ADOStoredProc组件与BDE中的组件StoredProc对应 10.6.2 通过ADO连接数据库1.构造连接字符串(1)在对象观察中选择数据集组件的ConnectionString属性,单击属性项右侧的符号按钮,打开连接字符串对话框2)连接字符串对话框中有两个单选项,提供了两种不同的连接方式●使用数据链接文件:数据链接文件是一个扩展名为.UDL的文件,其中存放了一个连接字符,用户可以预先建立数据链接文件,以便连接字符串能够重复利用。
●使用连接字符串:选择了该项就需要自己创建一个连接字符串可以在相应的文本框中键入一个连接字符串,但一般来说,还要进行数据引擎的选择,所以,应单击Build按钮,打开数据链接属性对话框 3)选择数据库引擎在对话框的Provider页,选择要连接的OLE DB提供者 (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数据库与数据源创建1.BDE方式(1)选择【开始】|【程序】|【Borland Delphi7】|【BDE Administrator】,即可打开BDE管理器2)单击BDE管理界面的下拉菜单中【Object】|【New】选项,开始创建一个新的BDE数据别名,首先要选择数据库驱动程序名,因为选择采用Paradox数据库格式,所以选择STANDARD驱动 (3)单击【OK】按钮,就增加了一个默认名为STANDARD1的设置项 (4) 修改Paradox驱动设置单击【Configuration】页进行数据库驱动设置,单击【Native】|【Paradox】,出现Paradox数据表驱动的配置项,修改【LangDriver】中的值 (5) 已经设置好了数据库别名,要使该别名立即生效,单击【Object】项中的Apply,这样别名就设置完毕6)接下来要创建Operator数据表,选择【开始】|【程序】|【BorlandDelphi7】|【Database Desktop】,即可打开数据表管理窗口 。
(7)单击【File】|【New】|【Table】选项,出现数据表选择对话框,选择Paradox7 8)单击【OK】按钮,就进入了创建数据表的对话框输入字段信息 9)单击【Save As】按钮,将保存已创建的数据表结构,这时提示输入数据表名可以通过Alias选择已定义的别名,并且在文件名栏中输入要命名的文件名 10)单击【保存】按钮即可,可以通过点击Database Desktop数据库管理器中【File】|【open】|【Table】可以浏览刚创建的空的数据表 2.ODBC方式(1)选择【开始】|【程序】|【Microsoft office 2003】|【Microsoft office Access 2003】,即可打开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中各种网络组件的强大功能,都是建立在WinSock API基础之上的11.2 WinSock基础基础 11.2.1 TCP、UDP和IP协议 对应于OSI七层模型,TCP和UDP协议是位于传输层的协议,而IP则位于网络层如图所示: OSI模型和网际协议族 TCP是传输控制协议,它是一种面向连接的协议,它向用户提供可靠的全双工的字节流。
TCP关心确认、超时和重传等具体细节大多数Internet应用程序使用TCP,因为它是一种精致的、可靠的字节流协议应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 应用层TCP UDP IP 驱动程序和硬件 UDP是用户数据报协议,它是一种无连接协议UDP是一种简单的,不可靠的数据报协议,与TCP不同,UDP不能保证每一个UDP数据报可以到达目的 IP是网际协议Ipv4(我们通常就称之为IP)自80年代早期以来一直是网际协议族的主力协议,它使用32位地址,为TCP、UDP、ICMP和IGMP提供递送分组的服务90年代中期,又设计出了用以替代Ipv4的Ipv6,它使用128位的大地址11.2.2 套接口(Socket)和Winsock API 套接口(Socket)最初是由加利福尼亚大学Berkeley学院为UNIX操作系统开发的网络通信编程接口 90年代初,由Microsoft、Sun Microsystems等几家公司共同参与制定了一套标准,即Windows Sockets规范1993年,他们制定了Windows Sockets1.1规范,定义了16位Windows平台下的网络标准编程接口。
对应于OSI七层参考模型,WinSock API是位于会话层和传输层之间的,它提供了一种可为指定的传输协议(如TCP、UDP)打开、进行和关闭会话的能力Windows Sockets规范已经成为在Windows平台上开发网络应用程序的已接受标准,它也为想开发网络程序的程序员提供了巨大的方便 11.2.3 面向连接和无连接 面向连接发服务中,进行数据交换之前,通信双方必须建立一条用以进行通讯的路径这样既确定了通讯双方之间的联系,又可以保证双方都处于活动状态,可以彼此响应多数情况下,面向连接的协议可以保证传输数据的可靠性 TCP协议即是一种面向连接的协议应用程序利用TCP进行通讯时,发起方和接受方之间会建立一个虚拟连接,通过这一连接,双方可以把数据当作一个双向的字节流来进行交换 无连接服务不管目的方是否处于待接收状态,源方只管将信息发送给目的方,也不管目的方是否已经接受到了该信息,以及接收到的信息是否无误 UDP协议即是一种无连接协议,数据传输方法采用的是数据报 面向连接服务和无连接服务各有优缺点 面向连接的服务能够保证通讯双方传递数据的正确性,但它却要为此进行额外的校验,同时,通讯双方建立通信信道也需要许多系统开销。
无连接服务最大的优点就是速度快,因为它不用去验证数据完整性,也不为数据是否已经被接收操心 11.2.4 客户/服务器模式 当今网络应用中,通信双方最常见的交互模式便是客户/服务器模式在这种模式中,客户向服务器发出服务请求,服务器收到请求后为客户提供相应的服务 客户/服务器模式通常采用监听/连接方式实现一个服务器端的应用程序通常在一个端口监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务提出了连接请求此时,服务进行被“唤醒”并且为客户提供服务,即对客户的请求作出适当的反应11.2.5 套接口类型 在使用TCP/IP协议时,可选的套接口类型有三种:流式套接口、数据报套接口及原始套接口 流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输对于建立在这种类型套接口上的套接字来说,数据是可以双向传输的字节流,无长度限制 数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错即一个建立在数据报套接口上的套接字所接收的信息有可能重复,或者和发出时的顺序不同。
原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新网络协议实现的测试目前,只有WinSock 2提供了对它的支持在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()函数关闭套接字c11.3 网络聊天程序的实现网络聊天程序的实现11.3.1 使用TCP协议 由于网络的不稳定及经常丢失数等原因,在Internet上实现两台机器间的联系需要使用TCP基于TCP/IP协议的传输是面向连接的点到点的传输在聊天时,用户首先必须设置本地机器的端口号,打开本地TCP服务器,然后向远程主机发送连接请求,建立虚路连接,才能开始数据的传送与接受。
介绍TIdTCPClient、TIdTCPServer组件的用法 1.TIdTCPClient组件 TIdTCPClient组件位于Indy Clients组件板如图所示,TIdTCPClient组件客户方通信管理,封装了完整的SocketTIdTCPClient组件是许多Indy客户端组件的基类 1)TIdTCPClient组件主要属性介绍§BoundIP属性:用来指定客户端连接的本地IP地址图11-4 IdTCPClient组件§BoundPort属性:用来指定客户端连接的本地IP地址,在Connect方法中指定绑定端口号§Host属性:用于标识远程计算机地址,可以是IP地址,也可以是计算机名§Port属性:用于表示远程计算机端口,和Host属性一起用于指明远程计算机地址§ReadTimeout属性:用来指明读数据时的连接超时毫秒数§RecvBufferSize属性:指明了用来接收数据的缓冲区字节大小,默认值为8192§SendBufferSize属性:指明了写数据到连接缓冲区时允许的最大字节数,默认值为32768§LocalName属性:指明本地计算机名称(2)TIdTCPClient组件的过程和方法介绍§Connect 形式:procedure Connect(const ATimeout:integer); 含义:用来向服务器请求建立一个连接§ConnectAndGetAll 形式:function ConnectAndGetAll():sting; 含义:用于连接到Host属性和Port属性指定的远程计算机并从服务器中读取所有数据§Connected 形式:function Connected(): Boolean; 含义:用来指明到对方计算机的连接是否已经建立§OpenWriteBuffer 形式:procedure OpenWriteBuffer(const AThreshhold:Integer); 含义:打开写缓冲区§CloseWriteBuffer形式:procedure CloseWriteBuffer();含义:关闭写缓冲区§Disconnect形式:procedure Disconnect();含义:用于断开与对方计算机的连接§FlushWriteBuffer形式:procedure FlushWriteBuffer(const AByteCount: Integer);含义:发送自调用OpenWriteBuffer以来的所有缓冲区数据到对方计算机,并随后清空缓冲区§ReadBuffer形式:procedure ReadBuffer(var ABuffer; constAByteCount:Longint);含义:从Indy的接收缓冲区中读取的字节数§ReadStream形式:procedure ReadStream(AStream:TStream;AByteCount:LongInt;const AreadUntil Disconnect :Boolean);含义:从Indy缓冲中读取数据并存储在AStream参数指定的目的流中§WriteBuffer形式:procedure WriteBuffer(const Abuffer; AByteCount:LongInt;const AwriteNow:Boolean)含义:用于向对方计算机发送数据,数据被写往Indy缓冲区或者直接发送给对方§WriteStream形式:Procedure WriteStream(AStream:Tstream; const AAll:Boolean;const AWriteByteCount: Boolean; const ASize:Integer);含义:用于发送一个对象流(3)TIdTCPClient组件的事件介绍§OnStatus事件:是一个TidSatusEvent类型的时间句柄,在当前连接状态改变时被触发 2.TIdTCPServer组件 TIdTCPServer组件位于Indy Servers组件板如图所示,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形式:Procedure BeginWork(AWorkMode:TWorkMode;const Size:Integer);方法:用于触发OnBeginWork事件,同时维护读/写堵塞操作的数量,以及初始读写操作的大小信息§DoWork形式:procedure DoWork(AWorkMode:TWorkMode;const ACount:Integer);方法:用于触发OnWork事件。
在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不产生任何效果§EndWork形式:procedure EndWork(AWorkMode:TWorkMode);方法:EndWork过程用于触发OnEndWork事件EndWork上可以嵌套调用,但是on EndWork事件仅在第一次调用时触发§(3)TIdTCPCServer组件的事件句柄介绍§OnConnect事件:在客户线程试图连接到TCP服务器时触发§OnDisconnect事件:在客户线程试图断开与TCP服务器时触发§OnExecute事件:当客户线程试图执行TidPeerThread.Run方法时触发§OnStatus事件:在当前连接的状态改变时被触发11.3.2 使用UDP协议 首先介绍TIdUDPClient、TIdUDPServer组件的用法 1.TIdUDPClient组件 TIdUDPClient组件位于Indy Clients组件板如图所示,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形式:procedure Send (AHost:string;const APort :Integer; const AData :string);含义:AData中的数据传送给Host属性和Port属性指定的远程计算机§SendBuffer形式:procedure SendBuffer(AHost:string;const Port:Integer ; var ABuffer ; AByteCount: Integer);含义:用于传送数据给远程计算机§Boradcast形式:procedure Boradcast(const AData:string;const APort:Integer);含义:向网络中的所有计算机广播AData中的数据。
§ReceiveBuffer形式:function ReceiveBuffer(var ABuffer;const ABufferSize:Integer; var VPeerIP:string var VPeerPort: integer;AMsec:Integer):integer;含义:用于从VPeerIP 和VPeerPort参数指定的计算机中读出数据到ABuffer缓冲区§BeginWork形式:procedure BeginWork(AWorkMode:TWorkMode; const ASize: Integer);含义:用于触发OnBeginWork事件,同时维护读写堵塞操作的数量,以及初始读写操作的大小信息§DoWork形式:procedure DoWork(AWorkMode:TWorkMode; const ACount: Integer)含义:用于触发OnWork事件,在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不会产生任何效果§EndWork形式:procedure EndWork(AWorkMode:TWorkMode);含义:用于触发OnEndWork事件,EndWork可以嵌套调用,但是OnEndWork 事件仅在第一次调用时触发。
(3)TIdUDPClient的事件响应§OnStatus事件:在当前链接的状态改变时被触发2.TIdUDPServer组件 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形式:procedure Boradcast(const AData:string;const APort:Integer);含义:向网络中的所有计算机广播AData中的数据,Aport参数指明了计算机的端口号§ReceiveBuffer形式:function ReceiveBuffer(var ABuffer; const ABufferSize:Integer; var VPeerIP:string var VPeerPort: integer;AMsec:Integer):integer;含义:用于从VPeerIP和VPeerPort参数指定的计算机中读出数据到ABuffer缓冲中§Send形式:procedure Send (AHost:string;const APort :Integer; const AData :string);含义:Send过程将AData中的数据传送给由AHost参数和APort参数指定的远程计算机§BeginWork形式:Procedure BeginWork(AWorkMode:TWorkMode; const ASize:Integer);含义:该过程可以被嵌套调用,但是OnBeginWork 事件仅在第一次调用BeginWork 方法时触发。
§DoWork形式:procedure DoWork(AWorkMode:TWorkMode; const ACount:Integer);含义:用于触发OnWork事件在调用DoWork过程之前必须调用BeginWork过程,否则DoWork过程将不产生任何效果§EndWork形式:procedure EndWork(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)传送和(接收数据信号线,引脚3)接收的信息格式即一个传送单位(字节)由起始位、数据位、奇偶校验和停止位组成。
2.联络信号(RTS、CTS、 DTR、DSR、DCD和RI等6个信号) RTS(请求传送,引脚4),是PC向Modem发出的联络信号高电压表不PC机请求向Modem传送数据 CTS(清除发送,引脚5),是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发来的振铃信号3.SG(信号地) SG(信号地,引脚)为相连的PC和Modem提供同一电势参考点12.1.2 握手 DTE和DCE之间要实现双向通信,至少需要3条信号线:TxD使数据从DTE到ECE。
RxD使数据从ECE到ETE,SG为信号地 必须使用握手信号,它提供了一种控制数据流的方法,即接收设备可以控制发送设备的数据发送 在异步串行通信中,这称之为握手(handshaking)或流量控制(flow control)握手控制可以具体分为硬件握手(硬件流控)和软件握手(软件流控)1.硬件握手 硬件握手是使用专门的握手电路去控制数据的传输当接收设备准备好之后,就通过专用的握手电路传送一个正电压给发送设备,指示发送设备数据如果接收传送一个负电压给发送设备,则指示发送设备停止发送数据 为了完成数据通信需要有3类电路:数据线、信号线和握手线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以上版本和Windows 3.2/98/NT最多支持4个串口:COM1、COM2、COM3和COM4它们所占用的I/O口地址和中断号见表 :串口串口 I/O地址地址 中断号中断号 COM1 0x3f8 IRQ4 COM20x2f8 IRQ3 COM3 0x3e8 IRQ4 COM40x2e8 IRQ3 为一更好地说明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常用的串行通信操作函数1.CreateFileCreateFile创建或打开一下的对象并返回句柄 完整定义:Handle CreateFile(LPCTSTR lpFileName,//文件名DWORD dwDesireAccess,//访问模式(读/写)DWORD dwShareMode, //共享模式LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全属性DWORD dwCreationDistribution,//文件已经存在或不存在时的处理方法DWORD dwFlagsAndAttributes,//文件属性,对于串口来说有意义的属性只有FILE_FLAG_OVERLAPPED,表示端口的I/O可以在后台进行(后台IO也叫异步IO) HANDLE hDemplateFile//复制制定文件的扩展属性);2.CloseHandleCloseHandle函数关闭一个已打开的对象句柄,完整定义:BOOL CloseHandle( HANDLE hObject //句柄);3.SetupCommSetupComm为通信设备初始化参数(设置通信缓冲区的大小),完整定义;BOOL SetupComm(HANDLE hFile,//句柄DWORD dwInQueue,//输入缓冲区的大小DWORD dwOutQueue//输出缓冲区的大小);4.ReadFileReadFile同步或异步从文件读取数据,在读之前可能要调整文件指针的位置,完整定义:BOOL ReadFile( HANDLE hFile,//句柄LPVOID lpBuffer,//接收数据的缓冲区地址DWORD nNumberOfBytesToRead,//读取的字节数LPDWORD lpNumberOfBytesRead,//读取字节数的地址LPOVERLAPPED lpOverlapped//当打开文件制定dwFlagsAndAttributes参数为FILE_FLAG_OVERLAPPED时,这个参数就必须应用一个特殊的结构,结构中定义一次异步读操作。
否则,该参数应置为空);5.WriteFileWriteFile同步或异步写数据到文件中,在写之前可能要调整文件指针的位置,完整定义:BOOL WriteFile(HANDLE hFile,//句柄LPCVIOD lpBuffer,//指向缓冲区的数据DWORD nNumberOfBytesToWrite,//要写的字节数LPDWORD lpNumberOfBuffersWritten,//返回实际写的字节数LPOVERLAPPED lpOverlapped//当打开文件指定dwFlagsAndAttributes参数为FILE_FLAG_OVERLAPPED时,这个参数就必须引用一个特殊的结构,结构中定义一次异步写操作否则,该参数应置为空);6.Set CommStateSetCommState用制定的DCB结构设置通信参数,将重新初始化硬件和控制设置,但不会清空输入输出缓冲区DCB结构中包含波特率、数据位、校验位、停止位和流控制方式等信息完整定义:BOOL SetCommState( HANDLE hFile,//句柄LPDCB lpDCB//指向硬件控制块);7.GetCommStateGetCommState返回当前通信参数的DCB结构。
DCB结构中包含波特率、数据位、校验位、停止位和流控制方式等信息,完整定义:BOOL GetCommState(HANDLE hFile,//句柄LPDCB lpDCB//指向硬件控制块);8.ClearCommErrorClearCommError清除串口错误并获取当前状态(可以返回接收缓冲区中处于等待状态的字节数)完整定义:BOOL ClearCommError( HANDLE hFile,//句柄LPDWORD lpErrors,//接收错误代码LPCOMSTAT lpStat//指向通信设备的状态缓冲区);9.BuildCommDCBBuildCommDCB函数用制定的设备控制串填充DCB结构,设备控制串可用相应的模式控制命令得到要使设置生效,还需调用SetCommState完整定义:BOOL BuildCommDCB( LPCTSTR lpDef,//指向设备控制串LPDCB lpDCB//指向设备控制块);10.BuildCommDCBAndTimeoutsBuildCommDCBAndTimeouts函数用指定的设备控制串填充DCB结构,并设置超时值、未超时值设备控制串可用相应的模式控制命令得到。
这个函数综合了BuildCommDCB和SetCommTimeouts两个函数,完整定义:BOOL BuildCommDCBAndTimeouts(LPCTSTR lpDef,//设备控制串LPDCB lpDCB,//设备控制块LPCOMMTIMEOUTS lpCommTimeouts//超时结构);11.ClearCommBreakClearCommBreak函数恢复发送缓冲区中的数据传送,并把线路置为nonbreak状态(可参阅SetCommBreak和TransmitCommChar)完整定义:BOOL ClearCommBreak(HANDLE hFile//句柄);12.CommConfigDialogCommConfigDialog函数显示配置端口的对话框,完整定义:BOOL CommConfigDialog(LPTSTR lpszName,//设备名字字符串HWND hWnd,//窗口句柄LPCOMMCONFIG lpCC//Comm配置结构);13.DeviceIoControlDeviceIoControl函数直接发送控制指令到指定的设备,让设备执行特定的操作完整定义:BOOL DeviceIoControl(HANDLE hDevice,//句柄DWORD dwIoControlCode,//控制指令LPVOID lpInBuffer,//指定指令所需的数据缓冲区DWORD nInBufferSize,//lpInBuffer缓冲区的大小LPVOID lpOutBuffer,//指定指令返回的数据缓冲区DWORD nOutBufferSize,//lpOutBuffer缓冲区的大小LPDWORD lpBytesReturned,//lpOutBuffer缓冲区返回数据的实际大小LPOVERLAPPED lpOverlapped//指向Overlapped结构);14.EscapeCommFunctionEscapeCommFunction函数直接让设备执行指定的扩展操作,用于完全控制端口。
完整定义:BOOL EscapeCommFunction(HANDLE hFile,//句柄DWORD dwFunc //要执行的扩展功能);15.GetCommConfigGetCommConfig函数获得当前设备的设置,王政定义:Bool GetCommConfig(HANDLE hCommDev,//句柄LPCOMMCONFIG lpCC,//Comm配置结构地址LPDWORD lpdwSize//缓冲区大小);16.GetCommMaskGetCommMask函数返回指定的设备的事件掩码,完整定义:BOOL GetCommMask(HANDLE hFile,//句柄LPDWORD lpEvtMask//返回的事件掩码);17.GetCommModemStatusGetCommModemStatus函数返回Modem的控制寄存器的值,完整定义:BOOL GetCommModemStatus(HANDLE hFile,//句柄LPWORD lpModemStat//控制寄存器的值);18.GetCommPropertiesGetCommProperties函数返回指定设备的属性在调用SetCommState之前常用此函数判断是否支持指定的设置值,例如,是否支持的波特率等。
完整定义:BOOL GetCommProperties(HANDLE hFile,//句柄LPCOMMPROP lpCommProp//属性结构);19.GetCommStateGetCommState函数返回指定设备当前设置的设备控制块,完整定义:BOOL GetCommState(HANDLE hFile,//句柄LPDCB lpDCB//设备控制块);20.GetCommTimeoutsGetCommTimeouts函数返回指定设备的所有读写操作超时值,完整定义:BOOL GetCommTimeouts(HANDLE hFile,//句柄LPCOMMTIMEOUTS lpCommTimeouts//超时结构);21.GetDefaultCommConfigGetDefaultCommConfig函数返回通信设备的默认值配置,完整定义:BOOL GetDefaultCommConfig(LPCSTR lpszName,//设备名字符串LPCOMMCONFIG lpCC,//配置结构LPDWORD lpdwSize //结构的大小);22.PurgeCommPurgeComm函数取消输入或输出缓冲区的所有字符,并中止悬而未决的读或写操作,完整定义:BOOL PurgeComm(HANDLE hFile,//句柄DWORD dwFlags//取消操作的参数);23.SetCommBreakSetCommBreak函数暂停发送缓冲区的数据传送,并把线路为break状态,直到调用ClearCommBreak时才恢复。
完整定义:BOOL SetCommBreak(HANDLE hFile//句柄);24.SetCommConfigSetCommConfig函数设置通信设备的当前配置,完整定义:BOOL SetCommConfig(HANDLE hCommDev,//句柄LPCOMMCONFIG lpCC,//配置结构DWORD dwSize //结构的大小);25.SetCommMaskSetCommMask函数设置指定设备的事件掩码调用此函数后,需要再调用WaitCommEvent来等待事件的产生完整定义:BOOL SetCommMask(HANDLE hFile,//句柄DWORD dwEvtMask//事件掩码);26.SetCommTimeoutsSetCommTimeouts函数设置读和写操作的超时值,完整定义:BOOL SetCommTimeouts(HANDLE hFile,//通信设备句柄LPCOMMTIMEOUTS lpCommTimeouts//超时结构);27.SetDefaultCommConfigSetDefaultCommConfig函数设置通信设备的默认配置,完整定义:BOOL SetDefaultCommConfig(LPCSTR lpszName,//设备名字符串LPCOMMCONFIG lpCC,//配置结构DWORD dwSize//结构的大小);28.TransmitCommCharTransmitCommChar函数向指定设备发送字符,该字符将优先于输出缓冲区中的数据。
一般情况下,先调用SetCommBreak,再调用此函数,最后调用ClearCommChar,用于优先反送指定字符完整定义:BOOL TransmitCommChar(HANDLE hFile,//句柄Char cChar//发送的字符);29.WaitCommEventWaitCommEvent函数等待指定设备的事件发生一系列的事件被此函数监视,包括设备相关的事件掩码,可以同步或异步方式进行完整定义:BOOL WaitCommEvent(HANDLE hFile,//句柄LPDWORD lpEvtMask,//要处理的事件LPOVERLAPPED lpOverlapped,//Overlapped结构,用于异步方式);12.3 MSComm控件控件12.3.1 MSCom安装 MSCcomm组件是Microsoft Visual Studio配带的ActiveX组件,一般安装Microsoft Visual Studio后这些文件会自动生成,然后在Delphi中安装MSComm控件步骤如下:(1)先打开Delphi7.0集成开发环境,选择菜单“Component”中的“Import ActiveX Control”命令,在“Import AcitiveX”选项卡内选择“Microsoft Comm Control 6.0”项 。
2)单击“Install” 按钮安装MSComm 控件,安装后在“ActiveX”组件板中出现MSComm图标,即可被使用 MSCOMM32.OCX可以按如下两种方式注册:第一种方式:点击【开始】|【运行】,在运行命令栏中填入如下命令:Regsvr32 c:\windows\system\mscomm32.ocx第二种方式:打开记事本输入以下内容,并且保存未REG的扩展名,双击此文件也可以进行注册,REGEDIT4[HKEY_CLASSES_ROOT\Licenses\ 4250E830-6AC2-11cf-8ADB-00AA00C00905]@=” kjljvjjjoquqmjjjvpkqmqykypoqjquoun”12.3.2 MSComm 控件方法 MSComm 控件提供下列两种处理通信的方式:(1)事件驱动通信是处理串行端口交互作用的一种非常有效的方法在许多情况下,在事件发生时需要得到通知 2)在程序的每个关键功能之后,可以通过检查CommEvent属性的值来查询事件和错误如果应用程序较少,并且是自保持的,这种方法可能是更可取的 每个MSComm控件对应着一个串行端口。
如果应用程序需要访问多个串行端口,必须使用多个MSComm控件可以在Windows “控制面板”中改变端口地址和中断地址 尽管MSComm控件有很多重要的属性,但首先必须熟悉几个属性CommPort 设置并返回通信端口号Settings 以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位PortOpen 设置并返回通信端口的状态,也可以打开和关闭端口Input 从接收缓冲区返回和删除字符Output 向传输缓冲区写一个字符串12.3.3 MSComm控件属性 通信MSComm控件提供了27个关于通信控制方面的属性和5个标准属性 1.Break属性 描述:设置或清除中断信号的状态该属性在设计时无效 语法:[form .] MSComm.Break[:={True|False}] 设置为:True 设置中断信号状态False 清除中断信号状态2.CDHolding属性 通过查询Carrier Detect (CD)信号线的状态确定当前是否有传输 语法:[form.] MSComm.CDHolding[:={True|False}]CDHolding属性的设置值为:True Carrier Detect信号线为高电平False Carrier Detect信号线为低电平3.CommID属性 返回一个说明通信设备的句柄。
该属性在设计时无效,在运行时为只读 语法: [form .] MSComm.CommID4.CommEvent属性返回最近的通信事件或错误该属性在设计时无效,在运行时为只读语法:[form .] MSComm.CommEvent5.CommPort属性设置并返回通信端口号语法:[form .] MSComm.CommPort[:=value]6.CTSHolding 属性确定是否可通过查询Clear To Send (CTS) 信号线的状态发送数据 语法: [form .] MSComm.CTSHolding[:={True|False}]CTSHolding属性的设置值为:True Clear To Send信号线为高电平False Clear To Send信号线为低电平7.DSRHolding 属性确定 Data Set Ready (DSR)信号线的状态 语法:[form .] MSComm.CSRHolding[:={True|False}]CSRHolding属性返回以下值:True Data Set Ready信号线为高电平False Data Set Ready信号线为低电平8.DTREnable属性确定在通信时是否使Data Terminal Ready (DTR) 信号线有效。
语法:[form .] MSComm.DTREnable[:={True|False}]DTREnable属性设置值:True Data Terminal Ready信号线有效False Data Terminal Ready信号线无效(缺省) 9.EOFEnable属性EOFEnable属性确定在输入过程中MSComm控件是否寻找文件结尾(EOF)字符语法:[form .] MSComm.EOFEnable[:={True|False}]value的设置值:True 当EOF字符找到时OnComm事件被激活False 当EOF字符找到时OnComm事件不被激活(缺省)10.Handshaking属性设置并返回硬件握手协议语法:[form .] MSComm.Handshaking[:=value] 11.InBufferCount属性返回接收缓冲区中等待的字符数该属性在设计时无效语法:[form .] MSComm. InBufferCount[:=value]12.InBufferSize属性设置并返回接收缓冲区的字节数语法:[form .] MSComm. InBufferSize[:=value]13.Input属性返回并删除接收缓冲区中的数据流。
该属性在设计时无效,在运行时为只读语法:[form.] MSComm.Input14.InputLen属性设置并返回Input属性确定被Input属性读取的字符数语法:[form.]MSComm.InputLen[:=value]15.InputMode属性设置或返回Input属性取回的数据的类型语法:[form.]MSComm.InputMode[:=value]16.NullDiscard属性确定NULL字符是否从端口传送接收缓冲区语法:[form.]MSComm.NullDiscard[:=value]value设置值是:Ture NULL字符不从端口传送到接收缓冲区False NULL字符从端口传送到接收缓冲区(缺省值)17.OutBufferCount属性返回在传输缓冲区中等待的字符数,也可以用它来清除传输缓冲区该属性在设计时无效语法:[form.]MSComm.OutBufferCount[:=value]18.OutBufferSize属性以字节的形式设置并返回传输缓冲区的大小语法:[form.]MSComm.OutBufferSize[:=value]19.Output属性往传输缓冲区写数据流。
该属性在设计时无效,在运行时为只读语法:[form.]MSComm.Output[:=value]20.ParityReplace属性当发生奇偶校验错误时,设置并返回换数据流中一个非法字符的字符语法:[form.]MSComm.ParityReplace[:=value]21.PortOpen属性设置并返返回通信端口的状态(开或关)该属性在设计时无效,在运行时该才可用语法:[form.]MSComm.PortOpen[:=value]value设置值是:True 端口开False 端口关22.Rthreshold属性在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm之前,设置并返回要接收的字符数语法:[form.]MSComm.Rthreshold[:=value]23.RTSEnable 属性确定是否使Request To Send (RTS) 信号线有效 语法:[form.]MSComm.RTSEnable[:=value]value设置值:True Request To Send 信号线有效False Request To Send信号线无效(缺省)24.Settings属性设置并返回波特率、奇偶校验、数据位和停止位参数。
[form.]MSComm.Settings[:=value]25.MSComm控件设置CommEvent属性为comEvSend并产生OnComm事件之前,设置并返回传输缓冲区中允许的最小字符数语法:[form.]MSComm.SThreshold[:=value]12.3.4 MSComm控件事件的介绍OnComm事件无论何时当CommEvent属性的值变化时,就产生OnComm事件,标志发生了一个通信事件或一个错误语法:Procedure MSCommComm(Sender:Tobject);12.4 MSComm控件的错误消息控件的错误消息MSComm控件的错误(Error)常数见表 :常数常数 值值 含义描述含义描述 ComEventBreak 1001 接收到中断信号 ComEventCTSTO 1002Clear-to-send超时 ComEventDSRTO 1003Data-set ready超时 ComEventFrame 1004帧错误 ComEventOverrun 1006端口超速 ComEventCDTO 1007Carrier Detect超时 ComEventRxOver 1008接收缓况区溢出 ComEventRxParity 1009 错误 ComEventTxFull 1010传输缓冲区满 ComEventDCB 1011检索端口的设备控制块时出现的意外错误 第第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】在弹出的【New Items】对话框中,选择Tthread Objec图标,单击【OK】按钮,系统将自动创建一个Tthread Object2)系统弹出News Tthread Object对话框,在其中输入一个新的类名和线程名输入类名和线程名之后,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属性值设为True13.2.4 编写线程的清除代码 OnTerminate事件处理过程不作为用户线程的一部分运行,它在主VCL线程中运行,因此必须注意以下两点:(1)用户在OnTerminate事件处理过程中不能使用任何线程局部变量;(2)用户在OnTerminate事件处理过程中可以安全的访问任何组件以及VCL对象而不必担心与其他线程发生冲突。
13.3 使用线程对象13.3.1 线程的同步 VCL支持三种方法来避免其他线程与用户线程访问同样的内存区域 1.锁住对象2.使用临界区 临界区就像一个门,一次只允许一个线程进入要使用临界区,就要创建一个全局TcriticalSection对象该对象有两个方法:Acquire(阻塞其他线程执行临界区的代码)和Release(释放阻塞) 每个临界区都和用户要保存的全局内存相联系,每个线程在访问一个全局内存之前都应该首先调用Acquire方法以确保没有其他线程在使用它 3.使用multi—read—exclusive—write同步 当用户使用临界区保护全局内存时,每次只有一个线程可以使用该内存区,这样的保护可能满足不了用户的要求,特别是用户要求有一个必须经常读而很少写的对象或变量的时候 13.3.2 执行线程对象1.重载优先级 当程中指定它所能得到的CPU时间时,应在构造函数中指定线程的优先级然而,如果线程的优先级依赖于线程何时执行,就应该创建可以进入挂起状态的线程,设置线程的优先级,然后开始执行程序2.启动和停止线程 一个线程在运行前可以被启动和中止很多次要临时中止一个线程的执行,可以使用线程的Suspend方法。
用户可以调用Terminate方法要求一个线程提前停止,该方法将线程的Terminated属性设置为True 3.暂存线程 要暂存线程,用户必须维护一个已经创建的线程的列表,这个列表可以由使用线程的一个对象维护;另一个办法是用户可以使用一个全局变量来暂存线程13.4 利用多线程排序利用多线程排序1.界面设计 向窗体中增加3个Label、3个PaintBox和1个Button控件 2.程序设计第第14章章 面向对象程序设计面向对象程序设计 面向过程的程序设计着眼于系统实现的功能,采用自顶向下,逐步细化的方法进行功能分解直至建立系统的功能结构和相应的程序模块 类(Class)是具有相同属性和操作的对象的集合类是进行数据抽象的基本单位每一个对象都是类的一个实例(Instance) 所谓继承是父类(Base Class)(基类)可以派生自己的子类(Derived Class)(派生类),子类除了继承父类的属性和操作之外,还具有自己独特的属性和操作 通信是实现各个不同对象之间消息传递的方法所谓消息实际上是一个类的对象要求另一个类的对象执行操作的指令14.1对象的基本概念 14.1.1 对象的特性 一个对象,其最突出的特征有三个:封装性、继承性、多态性。
1.对象的封装性 对象的封装特性是把数据和代码结合在同一个结构中将对象的数据域封闭在对象的内部,使得外部程序必须而且只能使用正确的方法才能对要读写的数据域进行访问 2.对象的继承性 对象的继承性是指把一个新的对象定义成为已存在对象的后代新对象继承了旧类的一切东西 3.对象的多态性 多态性是在对象体系中把设想和实现分开的手段多态的含义是指某一个标识符表示多种类型的变量,或者标识不同意义的函数或过程 14.1.2 从一个对象中继承数据和方法 在窗体上单击鼠标或用Object Inspector的上端的Object Selector选中Form1对象,按
虽然这些方法的实际程序代码可能是在这个对象之外的程序库单元中,但这些方法仍然在这个对象的范围内,因为它们是在这个对象的声明部分中的声明的 当在一个对象的事件处理过程中编写程序代码来访问这个对象的属性值、方法或域时,不需要在这些标识符之前加上这个对象变量的名称 14.1.4 对象共有域和私有域的声明 可以在对象的Public或Private部分加入新的数据域和方法Public和Private是Object Pascal的保留字 在Pbulic部分中声明其他库单元中对象的方法也可以访问的数据域或方法在Private部分的声明有访问的限制如果在Private中声明域和方法,那么它在声明这个对象的库单元外是不透明的,而且不能被访问Private中可以声明只能被本可单元方法访问的数据域和本库单元对象访问的方法 14.1.5访问对象的域和方法 当想要改变一个窗体对象的一个域的某个属性,或是调用它的一个方法是,必须在这个属性名称或调用方法之前加上这个对象的名称 同样想改变一个窗体对象中一个对象域的多个属性或调用多个方法时,使用with语句可以简化程序With语句在对象中可和在记录汇总一样使用。
14.1.6对象变量的赋值 如果两个变量类型相同或兼容,可以把其中一个对象变量赋给另一个对象变量只要赋值的对象变量是被赋值的对象变量的祖先 类型,就可以将一个对象变量赋给另一个对象变量 14.1.7建立非可视化对象1.声明一个非可视化对象可以用如下的方法建立一个自己的TWorker非可视化对象Type TWorker=Class(TObject) Title:=String[20];Name:= String[20]; HourlyPayRate:real; Function CalculatePayAmount:real;end;2.用Create方法建立对象实例 TWorker只是一个对象类型除非通过一个构造函数的调用从而被实例取代或创建,否则一个对象并不存储在内存中构造函数是一个方法,它为新对象配置内存并且指向这个新的对象这个新的对象也被称为这个对象类型的一个实例 建立一个对象的实例,需要调用Create方法,然后构造函数把这个实例赋给一个变量如果想声明一个TWorker类型的实例,在访问这个对象的任何域之前,的程序代码必须调用Create。
Worker:= Tworker.Create;3.撤销对象 当使用完对象后,应该及时撤销它,以便把这个对象占用的内存释放出来可以通过调用一个注销方法来撤销的对象,它会释放分配给这个对象的内存 Delphi的注销方法有两个:Destroy和FreeDelphi建议使用Free,因为它比Destroy更为安全,同时调用Free会生成效率更高的代码可以用下列的语句释放用完的Worker对象: Worker.Free;14.2 类类型和对象类类型和对象 对象是类的实例(instance),即由类定义的数据类型的变量对象是实体,当程序运行时,对象为它们的内部表达占用一些内存对象与类的关系就像变量与类型的关系在Object Pascal中,声明类数据类型使用保留字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 声明特性 要声明特性,必须声明三件事情:特性名、特性的数据类型、读写特性值的方法Object Pascal使用保留字Property声明特性,其声明的一般格式为: Property <特性名特性名>:<类型类型>read<字段名字段名>/<方法名方法名> Write<字段名字段名>/<方法名方法名>; 特性的声明由保留字Property、特性名标识符,可选的特性接口(PropertyInterface)和特性限定符(Property Specifier)构成 特性接口指定特性的数据类型,参数和索引号。
14.4.2 特性限定符 特性限定符可以有4类,分别是Read,write,Stored和Default其中Read和Write限定符用于指定访问特性的方法或字段1.Read限定符Read限定符的语法格式为:Read <字段名字段名>/<方法名方法名> Read限定符用于指定读取特性的方法或字段,通常是一个不带参数的函数,返回的类型就是特性的类型,并且函数名通常以“Get”加特性名组成,如一个读取Caption特性的方法通常命名为GetCaption2.Wrire限定符Write限定符的语法格式为:Write <字段名字段名>/<方法名方法名> Write限定符用于指定修改特性的方法,通常是一个与特性同类型的过程,这个参数用于传递特性新的值,并且过程名通常以“Set”加特性名组成 在Write限定符指定的方法的定义中,通常首先是把传递过来的值跟原先的值比较,如果两者不同,就把传递过来的特性值保存在一个字段中,然后再对特性的修改作出相应的反应这样当下次读取特性值时,读取的总是最新的值如果两者相同,那就什么也不需要干3.Stored限定符Stored限定符的语法格式为:Storetrue true/false[default<属性缺省值属性缺省值>][nodefault]Stored限定符用于指定一个布尔表达式,通过这个布尔表达式的值来控制特性的存储行为 14.4.3 数组特性 所谓数组特性,即特性是个数组,它是由多个同类型的值组成的,其中每个值都有一个索引号,不过跟一般的数组不同的是,一般的数组是自定义类型,可以把数组作为一个整体参与运算如赋值或传递等,而对数组特性来说,一次只能访问其中的一个元素。
对于数组特性来说,可以使用Read和Write限定符,但Read利Write限定符只能指定方法而不能是字段,并且Object Pascal规定,Read限定符指定的方法必须是一个函数,函数的参数必须在数量和类型上与索引变量一一对应,其返回类型与数组特性的元素类型一致Write限定符指定的方法必须是一个过程,其参数是索引变量再加上一个常量或数值参数,该参数的类型与数组特性的元素类型一致 访问数组特性中的元素跟访问一般数组中的元素一样,也是用特性名加索引号14.4.4 特性的重载和重定义 所谓特性重载,就是在祖先类中声明的特性,可以在派生类中重新声明,包括改变特性的可见性(关于类成员的可见性将在后面详细介绍),重新指定访问方法和存储限定符以及缺省限定符等最简单的重载,就是在派生类中使用指令字Property,其一般格式为: Property<特性名特性名>; 类中可以重新定义一个与祖先类具有相同名称的属性,重定义相当于声明了一个新的属性,该属性隐藏了从祖先类中继承的同名属性属性声明中是否声明了属性类型是区别覆盖与重定义的唯一途径如果后代类中声明的属性带有类型,那就是重定义。
重定义一个属性必需给出完整的定义 不管在后代类中是通过重定义隐藏还是覆盖祖先类中的属性,属性的调用总是静态的14.5 类成员的可见性类成员的可见性 面向对象编程的重要特征之一就是类成员可以只有不同的可见性,在Object Pascal中,是通过这么儿个保留字来设置成员的可见性的:Published、Public、Protected、Private、Automated1.Private(私有):在Private部分声明的成员是私有的,它们只能被同一个类中方法访问,对于其他类包括它的派生类,Private部分声明的成员是不可见的,程序员不必知道类实现的细节,只需要关心类的接口部分 2.Public(公有):在Public声明的成员是公共的,也就是说,它们虽然在某个类中声明的但其他类的实例也可以引用,相当于C语言中的外部变量 3.Published:在Published部分声明的成员,其可见性与在Public部分声明的成员可见性是一样的,它们都是公共的,即这些成员可以被其他类的实例引用,Published和Public的区别在于成员的运行期类型信息不同一个Published元素或对象方法不但能在运行时,而且能在设计时使用。
4.Protected(保护):Protected与Private有些类似在Protected部分声明的成员是私有 的(受保护的),不同的是在Protected部分声明的成员在它的派生类中可见,并且成为派生类中的私有成员在Protected部分声明的成员通常是方法,这样既可以在派生类中访问这些方法,又不必知道方法实现的细节4.Automated:C++的程序员可能对这个保留字比较陌生,在Automated部分声明的成 员类似于在Public部分声明的成员,它们都是公共的,唯一的区别在于在Automated部分声明的方法和特性将生成OLE自动化操作的类型信息14.6 类类型的兼容性类类型的兼容性 一个类类型类与它的任何祖先类型兼容因此,在程序执行时,一个类类型变量既可以引用那个类型本身的实例,也可以引用任何继承类的实例14.7 VCL类结构与类结构与Tobject类类 14.7.1 VCL类结构VCL(Visual Component Library)是Delphi提供的一个可视化组件库该组件库是一个类库,包含了在Delphi程序设计中所用到的几乎所有的组件类结构这些类的对象都集成到了Dephi开发环境中(IDE)的组件面板上。
有些类是可视的,有些类是不可视的 VCL中主要类之间的继承关系 Tobject类是所有其他类的祖先 TObject 在System 单元声明,该类只定义了少数方法,包括一个基本的构造函数和析构函数 TObjectExceptionTStreamTPersistentTPrinterTListTGraphicsObjectTGraphicTComponententTCollectionTStringsTScreenTmenuTControlTCommonDialogTFieldTGraphicControlTWinControlTFormTScrollingWinControlTCustomControl图14-1 VCL主要类之间的继承关系 TPersistent是Tobject类的直接派生类,该类是一个抽象类,主要为它的继承者提供对流的读写能力TComponent类是TPersistent类的直接派生类,该类是VCL中所有组件类的祖先类该类定义了所有组件最基本的属性、方法和事件该组件类中包含可视的和不可视的组件 TControl直接派生于TComponent类,该类是所有在运行时可见的组件类的祖先类,该类中封装了可见组件的运行位置等信息。
在Delphi应用程序中使用比较多的TForm类就是TControl类的派生类 14.7.2 Tobject类 Tobject类是所有VCL类的祖先,对于用户自定义的类也是如此当用户在声明一个类类型时,如果不指定父类,Delphi将按照缺省情况指明Tobject类为父类 例如,对Tstudent类进行如下的定义:Type Tstudent=Class … End; 谢谢大家!谢谢大家!。
