您的位置:首页 > 编程语言 > Delphi

Delphi中异常的截获及其个性化处理

2007-08-30 17:41 281 查看
Delphi中异常的截获及其个性化处理

一、 异常及异常处理机制
  异常可以理解为一种特殊的事件,当这种特殊的事件发生时,程序正常的执行流程
将被打断。异常处理机制能够确保在发生异常的情况下,应用程序不会中止运行,并且
能以一致的风格进行处理。许多刚刚涉及Delphi的程序员感觉异常处理机制高深莫测,
是高级程序员的“专利”,总有尽可能绕开它的想法,因而不得不小心翼翼地检查每一
次函数调用的返回值,或者通过额外的代码捕获可能的错误,即使这样,也只能保证程
序代码不出错,而无法保证包括操作系统、设备驱动程序、数据库驱动程序、Delphi自
身的元件库和运行时间库在内的软硬件设备不出错。问题就在于,通过检查函数的返回
值或设置错误陷阱只能捕获可预见的错误,如果出现没有预见到的错误,或者函数调用
本身就已失败,程序正常的流程就会被打乱。由此可见,异常几乎是不可避免的。其实
,Object Pascal提供了多种异常处理方法,使异常的处理变得轻而易举,可以将你从为
你的应用程序执行的每个任务编写单独的错误处理程序的繁琐工作中解放出来。
二、 处理异常的方法
  1. Try...Except 结构
  Try...Except 结构是这样工作的:Try后面到Except之前的语句通常是希望正常执
行的代码,在执行过程中如果触发了异常,程序就跳入Except部分。在Except部分做出
的处理可以有以下三种类型:
  (a) 不需要确切知道异常的类型,只需要做笼统的处理时,可以弹出一个警告信息
框,或者释放已分配的资源,或者退出程序。程序示例如下:
  例1 Procedure TForm1.Button1Click(Sender :TObject)
  Var
  Num: Integer;
  Begin
  Try
 Num:=StrToInt(Edit1.Text);
  Edit2.Text:=IntToStr(Num*Num);
  Except
  ShowMessage(Edit1.Text+'无法转成整数!');
 End;
  End;
  该例中,点击Button1后,程序试图将编辑框Edit1中的内容转换为整型数,整数平
方之后再转换成字符串类型数据,在编辑框Edit2中显示。如果Edit1.Text是7.89,传递
给StrToInt将产生EConvertError异常,因为7.89不是一个有效整数。异常产生后,将显
示一条警告信息,并退出该过程。
  (b) 利用异常处理句柄处理某个特定的异常
  例如,在例1中可以针对EConvertError异常做出如下的处理:
  例2 Procedure TForm1.Button1Click(Sender :TObject)
  Var
  Num: Integer;
  Begin
  Try
  Num:=StrToInt(Edit1.Text);
  Edit2.Text:=IntToStr(Num*Num);
  Except
  On EConvertError Do
ShowMessage(Edit1.Text+'无法转成整数!');
  End;
  End;
  (c) 利用异常处理句柄处理多种异常
  通常,执行的代码不只产生一种异常,这就需要在Except部分进行更具体的测试,
根据产生的异常类型分别做出处理。现将例2稍加改动如下:
  例3 Procedure TForm1.Button1Click(Sender :TObject)
  Var
  Num: Integer;
  Begin
  Try
  Num:=StrToInt(Edit1.Text);
  Edit2.Text:=IntToStr(30 div Num);
  Except
  On EConvertError Do
ShowMessage(Edit1.Text+'无法转成整数!');
  On EDivByZero Do
  Begin
   ShowMessage('除数不能为零!');
   Edit2.Text:='0';
  End;
  End;
  End;
  在例3中,Try部分的第一条语句可能产生EConvertError 异常;当 Num为零时,Tr
y 部分的第二条语句还会产生被零除的异常(即产生EDivByZero 异常)。由于产生异常
原因不同,需要分别进行不同的处理。例3中的Except 部分的代码也可以写成以下的形
式,这两种异常处理的结果是一样的。
  Except
  On E:Exception Do
  Begin
  If E is EconvertErro Then
ShowMessage(Edit1.Text+'无法转成整数!');
  If E is EdivByZero Then
  Begin
  ShowMessage('除数不能为零!');
  Edit2.Text:='0';
  End;
 End;
  2. Try...Finally 结构
  Try...Finally 结构最大的用处是在异常发生的情况下,确保释放应用程序已经分
配的资源,或者完成一些必须的操作,比如:剪贴板Clipboard 在打开之后必须调用Cl
ose 方法将剪贴板关闭;数据感知组件更新禁止之后必须调用EnableControls方法才能
使更新显示有效等。Try...Finally 结构之所以能做到这一点,是因为不管异常是否发
生,程序都要执行Finally 部分。请看下面的例子:
  例4 Procedure TForm1.Button1Click(Sender :TObject)
  Var
  Icon : TIcon;
  Begin
  Try
  Icon:=TIcon.Create;
  Icon.LoadFromFile('Spin.ico');
  ImageList1.ReplaceIcon(0,Icon);
  Finally
  Icon.Free;
  End;
  End;
  在例4中,Try 部分代码是从磁盘获取一个图标,用来代替ImageList1 中的0号图标
。如果图标文件Spin.ico 不存在或者被损坏,将会产生EFOpenError 异常,中断 Try
部分代码的执行,但是程序会执行Finally 部分的代码,使得Icon 占用的资源得以释放
。当然,如果没有发生任何异常,Finally 部分也将被执行。可以说,Finally 部分是
Try...Finally 结构的“必经之路”。
  3. Try...Except 与Try...Finally 的嵌套结构
  Try...Finally 结构确实有它独到的功能,但是Try...Finally结构中没有 Try...
Except 结构中的异常处理句柄,也就无法知道当前确切的异常类型,并且不论异常是否
发生,程序都执行 Finally 部分,这就决定了 Try...Finally 结构无法对发生的异常
进行特别的处理。如果在应用程序中,既需要完成一些必要的操作,又要对发生的异常
进行处理,那么最好使用这两种结构的嵌套。请看下例:
  例5 Procedure TForm1.Button1Click(Sender :TObject)
  var
  Icon : TIcon;
  Begin
  Try
  Try
   Icon:=TIcon.Create;
   Icon.LoadFromFile('Spin.ico');
   ImageList1.ReplaceIcon(0,Icon);
   Finally
   Icon.Free;
   End;
  Except
  On EFOpenError Do
ShowMessage('无法打开图标文件Spin.ico');
  End;
  End;
  在例5中,无论异常发生与否,都将执行Finally 部分释放图标占用的资源。一旦异
常发生,则产生警告信息。上例是在 Try...Except 结构的 Try 部分嵌套 Try...Fina
lly 结构,也可以在 Try...Finally 结构的 Try 部分嵌套 Try...Except 结构,前者
是先执行必须的操作,再进行异常的处理,而后者是先进行异常的处理,再执行必须的
操作。在多数情况下,两者的区别并不大。除此之外,Try...Except 结构和Try...Fin
ally 结构还可以各自嵌套。在复杂的应用程序中,Except 部分或Finally部分的异常处
理代码本身又有可能触发异常,这就需要采用嵌套结构编写更深一层的异常处理代码,
这里就不再详细举例了。
  4. 定义自己的异常类
  我们的探讨将进入更深的一层——定义自己的异常类。虽然在简单的应用程序中,
程序员并无太大必要定义自己的异常类,但是它对编写个性化的异常处理程序仍然是有
帮助的。例7中的过程是对数据集 Table1 中的字段Name进行合法性检查,如果Name 没
有值或为空字符串,则产生异常,这其中就定义了一个 Exception 的派生类EMyExcept
ion。
  例6
  Type EMyException=Class(Exception); {定义自己的异常类EMyException}
  Procedure TForm1.Table1NameValidate(Sender : TField);
  Begin
  If ( Sender.IsNull ) or ( Sender.AsString='') Then
   Raise EMyException.Create('必须填写名称');
  End;
  三、个性化的异常处理句柄
  Delphi会自动处理大部分的异常,但全英文信息提示会让应用程序的最终用户感觉
并不友好。那么如何编写自己的异常处理代码呢? 在Forms单元中声明的TApplication
类中的OnException 事件为编写自己的异常处理代码提供了可能。事实上,用于OnExc
eption 事件的事件句柄就是应用程序默认的异常处理句柄。请看下面的例子。
  在 Project Resource 中,首先创建一个新类 TMyClass, TMyClass 中有一个MyEx
ceptionHandler 方法,可以根据应用程序的需要来编写,这里体现了个性化的异常处理
。由MyExceptionHandler事件来响应Application 的 OnException事件,这样在程序发
生异常时,首先调用的就是自己编写的异常处理事件MyExceptionHandler事件。工程文
件如下:
  例7
  program ExceptPrj;
  uses Forms, Windows, SysUtils, Classes, Dialogs, DB, DBTables,
   Example in 'Example.pas' {Form1};
  {创建一个TObject的派生类TMyClass}
  Type
   TMyClass=Class(TObject)
   Public
   Procedure MyExceptionHandler(Sender: TObject; EInstance: Exception)
;
   end;
  {编写自己的异常处理句柄}
  procedure TMyClass.MyExceptionHandler(Sender: TObject; EInstance: Except
ion);
  var
   ErrorFile:TextFile;
   FileName,Content:string;
FindFlag:Boolean;
  Begin
  {截获出现的新的异常,并存入文件ErrorInfo.txt.}
   FileName:=ApplicationPath+'ErrorInfo.txt'; { ApplicationPath 是在主
Form中定义的全局变量,记录应用程序所在的目录}
  {打开文件}
   AssignFile(ErrorFile,FileName);
   if not FileExists(FileName) then
   ReWrite(ErrorFile)
   else
   ReSet(ErrorFile);
  {检查出现的异常是否曾经记录在文件ErrorInfo.txt中}
   FindFlag:=False;
   While not SeekEof(ErrorFile) do
   begin
   Readln(ErrorFile,Content);
   if Pos(EInstance.ClassName+':'+EInstance.Message,Content)<>0 th
en
   begin
   FindFlag:=True;
   break;
   end;
   end;
   {如果是一个未被记录过的异常,则将它记录在文件中}
   if not FindFlag then
   begin
   ShowMessage('出现新的错误!');
   Append(ErrorFile);
   Writeln(ErrorFile,EInstance.ClassName+':'+EInstance.Message);
   end;
   {关闭文件}
   CloseFile(ErrorFile);
  {对出现的异常显示中文提示}
   If EInstance is EDivByZero then
   Application.MessageBox('除数不能为零!','错误',MB_OK+MB_ICONSTO
P)
   else if EInstance is EAccessViolation then
   Application.MessageBox('访问了无效的内存区域!','错误',MB_OK+MB_
ICONSTOP)
   else if (EInstance is EDatabaseError) then
   Application.MessageBox('数据库操作出现错误!','错误',MB_OK+MB_I
CONSTOP)
   else if (EInstance is EFOpenError) then
   Application.MessageBox('文件不能打开!','错误',MB_OK+MB_ICONSTO
P)
   else if (EInstance is EConvertError) then
   Application.MessageBox('非法的类型转换!','错误',MB_OK+MB_ICONS
TOP)
   else
   MessageDlg(EInstance.ClassName+':'+EInstance.Message,mtInformat
ion,[mbOK],0)
  end;
  {$R *.RES}
  var
   MyObject: TMyClass; {声明TMyClass类的一个变量}
  begin
   Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   ApplicationPath:=ExtractFilePath(Application.ExeName);
   MyObject:=TMyClass.Create; {创建TMyClass类的一个实例}
   Application.OnException:=MyObject.MyExceptionHandler; {响应OnExcept
ion事件}
   Application.Run;
  end.
  例7中的MyExceptionHandler 事件是我们在开发铁路车站行车工作细则管理系统时
编制的,由于篇幅所限,在此仅将这一事件的整体框架呈现给大家,希望通过这个例子
来说明开发异常处理句柄的方法。
  到此为止,我们对Delphi异常处理的几种方法由浅入深地进行了归纳,并对应于各
种方法列举了实例,希望对异常处理怀有“胆怯”心理的编程朋友有所帮助。当然,在
遇到具体问题的时候,还需要您选择最适合的异常处理方法。 [!21ki
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  职场 Delphi 休闲