VBA的错误---处理

来源:百度文库 编辑:神马文学网 时间:2024/04/29 09:00:50

VBA的错误处理

2009年7月25日评论 发表评论

本文摘自MSDN的MS Office97 Visual Basic Progammer’s Guider的第14章,调试部分没有包括在这里。不知道哪位老兄翻译的。对错误处理很有帮助的一篇文章。

无论你怎样认真细致地编写代码,错误总会(可能会)发生。理论上讲,Visual Basic过程根本不需要错误处理代码。然而不幸的是,有时会出现错误删除文件、磁盘驱动器空间满或网络驱动器意外断开等情况,这就存在导致代码中发生运行时错误的可能性。为处理这些错误,需要在你编写的过程中添加错误处理代码。

有时,错误还可能在代码内部发生;这种类型的错误通常被称作bug(错误)。小的错误会带来失败或不便。更严重的错误会造成应用程序对命令响应的中断,可能需要用户重新启动应用程序并造成未保存的所有工作被丢失。

对应用程序中的错误进行定位和更正的过程被称作调试。Visual Basic 提供一些帮助分析应用程序运行的工具。这些调试工具对于错误源的定位尤其有用,同时你还可使用这些工具试着对你编写的程序进行修改或者学习其他应用程序的工作方式。

本章对Visual Basic 中调试工具的使用进行了说明,解释了运行时错误(在代码正在运行时发生的错误,它是在试图进行非法操作时产生的)的处理方法。

注:本章中的信息适用于Microsoft Excel 97、Word 97和PowerPoint 97中的Visual Basic 编辑器。有关Microsoft Access 97中调试Visual Basic 代码和处理错误的信息,请参阅icrosoft Access 97和Microsoft Office 97开发者版中的“建立Microsoft数Access 97应用程序”。另外,“建立Microsoft Access 97应用程序”的联机版本可从Microsoft Access 97和Microsoft Office 97 专业版CD-ROM的Value Pack目录下找到。

如何处理错误

理论上讲,Visual Basic 过程根本不需要错误处理。事实表明,硬件问题或用户的意外操作会导致运行时错误,从而停止代码的运行,并且用户通常无法恢复应用程序的运行。

其他错误不会中断代码的运行,但会导致代码不按预想运行。

例如,下面程序中,若文件存在则返回真,若文件不存在则返回假,但程序中不包括错误处理代码。

Function FileExists (filename) As BooleanFileExists = (Dir(filename) <> "")End Function

Dir函数返回第一个与指定文件名(有或没有通配符、驱动器名或路径)匹配的文件;如果未找到匹配文件,返回长度为零的字符串。

这个代码包含Dir调用可能出现的任何结果。然而,如果变量指定的驱动器号是非法的驱动器,会出现错误“驱动器无效”。如果指定的驱动器是软盘驱动器,该函数仅在软盘驱动器中有盘和软驱门关上的情况下运行正常,否则Visual Basic 会出现“磁盘未准备好”的错误提示,并且停止代码的运行。

例如,下面示例可解决诸如驱动器无效或软盘驱动器中无盘之类的驱动器问题。

Function FileExists (filename) As BooleanDim Msg As String' 打开错误陷井,这样错误处理程序可对检测到的任何错误作出响应。   On Error GoTo CheckErrorFileExists = (Dir(filename) <> "")' 如果没有错误发生,不执行错误处理程序。          Exit FunctionCheckError:' 如果发生错误,转移至此。   ' 定义代表Visual Basic 内部错误代码的常量。   Const mnErrDiskNotReady = 71, mnErrDeviceUnavailable = 68' vbExclamation, vbOK, vbCancel, vbCritical, and vbOKCancel 是   ' VBA类型库中定义的常量。   If (Err.Number = MnErrDiskNotReady) ThenMsg = " 在软驱中插入软盘并关上软驱门。"' 显示一个带有感叹号符和OK、Cancel按钮的消息框。          If MsgBox(Msg, vbExclamation & vbOKCancel) = vbOK ThenResumeElseResume NextEnd IfElseIf Err.Number = MnErrDeviceUnavailable ThenMsg = "该驱动器或路径中不存在:" & filenameMsgBox Msg, vbExclamationResume NextElseMsg = "意外错误#" & Str(Err.Number) & " 发生: " _ & Err.Description'显示带有Stop(停止)符图标和OK按钮的消息框。      MsgBox Msg, vbCriticalStopEnd IfResumeEnd Function

在本示例代码中,Err对象的Number属性包含与发生的运行时错误相关的数字;Description属性包含对错误的简短描述。

若Visual Basic 产生“磁盘未准备好”错误,本示例代码将显示一个提示信息,告诉用户选择OK按钮或Cancel按钮。如果用户选择OK,Resume语句可返回发生错误的语句,并试着重新运行该语句。如果用户已经纠正了问题,则运行可顺利进行,否则将返回到错误处理程序。

如果用户选择Cancel按钮,Resume Next 语句从紧随产生错误的语句的下个语句恢复运行(本例中为Exit Function语句)。

若产生“驱动器无效”错误,本示例代码将显示一个描述该问题的信息。随后Resume Next语句使得该过程从紧随产生错误的语句的下个语句恢复运行。

如果发生意外错误,将显示对该错误的简短描述,代码停止在Stop语句。

你编写的应用程序可以更正错误或者提示用户修改引起错误的条件。要做到这点,需要使用诸如前一示例中说明的技巧。以下部分将详细探讨这些技巧。

错误处理程序的设计

错误处理程序是在你编写的应用程序中捕获错误和作出响应的例程。你应在你认为可能出现错误的过程中添加错误处理程序(除非你可明确判定,否则应假定任何Visual Basic 语句都可能产生错误)。错误处理程序设计的步骤如下:

  1. 设置或使一个错误陷井有效,以告诉应用程序发生错误时转移到何处(错误处理例程将运行)。On Error语句使错误陷井有效,并为应用程序指定错误处理例程从标有标签处开始。在前面的示例中,FileExists函数包含一个名为CheckError的错误处理例程。
  2. 编写一个响应你可预测的所有错误的错误处理例程。如果在某个点上,控制流实际转移到陷井中,此时陷井处于激活状态。CheckError例程使用一个If…Then…Else语句来响应Err对象的Number属性值(Number属性是响应Visual Basic 错误的一个数值参数),从而实现错误的处理。示例中,在产生“磁盘未准备好”的错误时,为用户显示要求关上软驱门的信息提示;在产生“驱动器无效”的错误时,为用户显示另一信息提示。如果发生其他错误,则显示相应的描述,并中止程序。
  3. 退出错误处理例程。出现“磁盘未准备好”的错误时,Resume语句使代码转回到发生错误的语句处。然后Visual Basic 将试着重新运行那个语句。如果条件未发生改变,就会再次发生错误,并转回到错误处理例程。出现“驱动器无效”的错误时,Resume Next语句使代码转回到紧接着发生错误的语句之后的语句处。

本主题的余下部分对如何执行这些步骤进行了详细论述。在阅读这些步骤时,请参阅前面示例中的FileExists函数。

设置错误陷井

当Visual Basic 运行错误处理程序指定的On Error语句时,错误陷井被激活。当包含错误陷井的过程处于激活状态时,该错误陷井是有效的—也就是说,在运行Exit Sub、Exit Function、Exit Property、End Sub、End Function或End Property语句前,过程中的错误陷井是有效的。在任何给定的过程中,任何时候都只能有一个错误陷井有效,你可创建多个备用的错误陷井,并在不同时间内分别激活它们。使用On Error语句的特殊形式On Error GoTo 0来禁止某一错误陷井。

使用On Error GoTo line语句,设置从错误陷井跳转到一个错误处理程序中,其中line 参数指定识别错误处理代码的标签。在FileExists函数示例中的标签为CheckError(虽然冒号是标签的组成部分,但在On Error GoTo line语句中不用冒号)。

编写错误处理例程

编写错误处理例程的第一步是添加行标签,以标识错误处理例程的开始。行标签应具有一个描述性名称,而且必须在其后加上冒号。编写时的习惯做法是把错误处理代码置于过程的结尾处,即行标签在Eixt Sub 、Exit Function或Exit Property语句后。这可避免过程中无错误发生时错误处理代码的执行。

错误处理例程的主体包括实际处理错误的代码,通常采用Select Case或If …Then…Else语句。你需要确定可能发生哪些错误,并为每个错误提供相应的处理措施,如在出现“磁盘未准备好”错误时,提示用户插入磁盘。通常需要使用Else或Case Else条件从句来提供一个处理任何预计外错误的选项,在前面的FileExists函数示例中,该选项是警告用户并结束应用程序。

Err对象的Number属性包含一个代表最近发生的运行时错误的数值代码。Err对象与Select Case或If…Then…Else语句的结合使用,可使你对发生的任何错误采取特定的措施。

退出错误处理例程

在FileExists函数示例中,错误处理程序使用了Resume语句,以从产生错误的语句处恢复运行;使用Resume Next语句,以从紧随产生错误语句的下一语句处恢复运行。还可采用其他方法来退出错误处理例程。你可根据情况使用下列表中的任何语句。

Resume [0] 程序从产生错误的语句恢复运行或从最近一次调用包含错误处理程序的过程的语句处恢复运行。使用该语句可在更正产生错误的条件后恢复运行。

Resume Next 程序从紧随产生错误的语句的下个语句恢复运行。如果错误发生在包含错误处理程序的过程以外,且被调用的过程不包含有效的错误处理程序,则从紧随调用发生错误的过程的语句之后的语句处恢复运行。

Resume line 程序在 line 参数指定的标签处恢复运行。line 参数是行标签(或非零行号),它必须和错误处理程序在同一个过程中。

Err.Raise Number:=number 触发运行时错误。当这个语句在错误处理例程中运行时,Visual Basic 搜索另一错误处理例程的调用列表(调用列表是被调用过程链,通过它,可到达当前的执行点。这方面的详细信息,参阅本章后面的“错误处理层次结构”)。

Resume与Resume Next间的差异

Resume 与Resume Next之间的差别在于:Resume是从产生错误的语句恢复应用程序的运行;Resume Next是从紧随产生错误的语句的下个语句恢复应用程序的运行。一般而言,对于错误处理程序可以更正的错误使用Resume,对于错误处理程序不能更正的错误使用Resume Next。你可编写错误处理程序,使存在的运行时错误不会让用户看到或者为用户显示错误信息并允许用户进行更正。

在下面的示例中,使用错误处理来进行其变量的“安全”除法,而不显示可能发生的错误。进行除法时可能发生的错误如下表所示。

错误                                     原因

“被零除”                            分子为非零数,但分母为零。
“溢出”                                分子、分母均为零(在浮点数除法中)。
“非法过程调用”              分子和/或分母是非数值变量(或不能被看作数值变量)。

对于所有这三种情况,下面示例为这些错误设置陷井并返回Null(空)。

Function Divide (numer, denom) as VariantConst mnErrDivByZero = 11, mnErrOverFlow = 6, mnErrBadCall = 5On Error GoTo MathHandlerDivide = numer / denomExit FunctionMathHandler:If Err.Number = MnErrDivByZero Or Err.Number = ErrOverFlow _Or Err = ErrBadCall ThenDivide = Null '如果发生被零除、溢出或非法过程调用错误,返回Null。   Else' 显示意外错误信息      MsgBox "意外错误 " & Err.Number & ": " & _Err.Description, vbExclamationEnd If           ' 对于所有情况都继续执行Resume Next。   Resume Next             ' 从Exit Function语句执行。End Function

在特定行恢复执行

Resume Next还可用在循环中发生的错误,你可以重新启动运行或使用Resume line语句控制程序转到某特定行标签处。

下面示例说明了Resume line 语句的使用。与前面的FileExists示例不同的是,这个函数允许用户输入文件名称,当文件存在时函数返回该文件名称。

Function VerifyFile As StringConst mnErrBadFileName = 52, mnErrDriveDoorOpen = 71Const mnErrDeviceUnavailable = 68, mnErrInvalidFileName = 64Dim strPrompt As String, strMsg As String, strFileSpec As StringstrPrompt = "输入检查的文件名称:"StartHere:strFileSpec = "*.*"      ' 以一默认的名称开始。   strMsg = strMsg & vbCRLF & strPrompt'让用户更改默认名称。   strFileSpec = InputBox(strMsg, "文件查找", strFileSpec, 100, _100)' 如果用户删除默认名称,退出。   If strFileSpec = "" Then Exit FunctionOn Error GoTo HandlerVerifyFile = Dir(FileSpec)Exit FunctionHandler:Select Case Err.Number      ' 分析错误代码并加载信息。          Case ErrInvalidFileName, ErrBadFileNamestrMsg = "你输入的文件名称无效,请重试一次。"Case MnErrDriveDoorOpenstrMsg = "关上磁盘驱动器门,重试一次。"Case MnErrDeviceUnavailablestrMsg = "你所指定的驱动器未找到,重试一次。"Case ElseDim intErrNum As IntegerintErrNum = Err.NumberErr.Clear                 ' 清除Err对象。             Err.Raise Number:= intErrNum ' 重新生成错误。   End SelectResume StartHere     '跳转回StartHere标签,使用户可重新输入另一文件名。End Function

如果找到一个与输入的文件名相匹配的文件,函数返回该文件名。如果未找到匹配的文件,函数返回一个零长度的字符串。如果意料中的错误发生,一条信息将被指定给strMsg变量,并跳转回标签StartHere重新执行,这使得用户有机会输入一个有效的路径和文件名称。

如果出现意外的错误,在Case Else中重新生成错误,使得调用列表中另一错误处理程序可捕获错误。这样做是有必要的,因为如果不重新生成错误,代码就会继续从Resume StartHere行运行,而如果重新生成错误,你可有效地防止错误的重复发生;新错误将被设置在调用堆栈的另一层。

错误处理层次结构

一个有效的错误处理程序是由执行On Error语句激活且未被关闭(可由On Error GoTo 0语句关闭,也可从其所在的过程中退出)的错误处理程序。一个活动的错误处理程序是当前正执行的错误处理程序。错误处理程序只有在有效时才能被激活,但并不是所有有效的错误处理程序是活动的。例如,Resume 语句后,处理程序处于不活动状态,但它仍是有效的。

当缺少有效错误处理例程的过程中发生错误或者在活动的错误处理例程中发生错误时,Visual Basic 将在调用列表中搜索另一有效的错误处理例程。调用列表是一个指向当前执行过程的调用顺序表,它在“调用堆栈”对话框上显示。只有在中断模式中(暂停应用程序的执行),单击“视图”菜单上的“调用堆栈”来显示“调用堆栈”对话框。

调用列表的搜索

假定调用按下列顺序发生:

  1. 某事件过程调用过程A。
  2. 过程A调用过程B。
  3. 过程B调用过程C。

当过程C正在执行,其他过程处于运行状态。如果在过程C中发生一个错误且其中没有一个有效的错误处理程序,那么 Visual Basic 会在调用列表所列的运行过程中向后搜索—先搜索过程B,然后是过程A,最后是起始事件过程(到此为至)—并运行第一个搜索到的有效错误处理程序。如果在整个调用列表中未找到一个有效的错误处理程序,则会显示一个默认的意外错误信息并暂停执行。

如果Visual Basic 找到一个有效的错误处理例程,继续执行该例程,如同错误发生在包含错误处理程序的同一过程中一样。如果错误处理例程中的Resume或Resume Next语句运行,则按下表所示继续执行。

语句                       结果

Resume               从Visual Basic 最近一次调用包含错误处理程序的过程的语句处恢复运行。在前面给定的调用列表中,如果过程A有一个包含Resume语句的有效错误处理程序,Visual Basic从调用过程B的语句恢复运行。

Resume Next    对最近一次调用包含错误处理程序的过程的语句,从紧随该语句之后的语句处恢复运行。在前面给定的调用列表中,如果过程A有一个包含Resume Next语句的有效错误处理程序,运行会返回到调用过程B语句的下一语句。

注意是从找到错误处理过程的过程中的语句处恢复运行,而没有必要从错误发生的过程中恢复运行。如果你没有考虑到这一点,你编写的代码可能按你无法预料的方式运行。为了便于调试代码,可采用简单的方法,即在发生错误时转入中断模式,这方面的内容见本章后面的“关闭错误处理” 部分。

如果错误处理程序的错误范围未包括实际所遇到的所有错误,在包含有效错误处理程序的过程中会发生意外(预料外)错误。在这种情况下,除非错误处理程序运行Resume语句,不然过程会无止境运行下去。为预防这类情况的出现,可在处理程序的Case Else语句中使用Err对象的Raise方法,它通常在错误处理程序内重新生成一错误,从而强制Visual Basic 在调用列表中搜索能处理该错误的处理程序。

搜索调用列表的返回效果很难预测,这是因为搜索效果取决于可解决错误的处理程序中Resume和Resume Next语句的运行。Resume从调用包含错误处理程序的过程的语句恢复运行。Resume Next从紧随调用包含错误处理程序的过程的语句的下一语句恢复运行。

例如,在前面讨论的调用列表中,如果过程A有一个有效的错误处理程序,而过程B和过程C没有,那么发生在过程C中的一个错误将由过程A中的错误处理程序进行处理。如果过程A中的错误处理程序在退出前使用Resume语句,那么程序将从调用过程的语句继续运行(即进入过程 B);如果过程A中的错误处理程序在退出前使用Resume Next语句,那么程序将从过程A中调用过程B的下一语句继续运行。在这两种情况中,错误处理程序都不直接返回到发生错误的源过程和语句。

复杂错误处理的准则

在编写包含多个模块的大型Visual Basic 应用程序时,错误处理代码可能变得相当复杂。请记住下面这些准则:

  • 在进行代码的调试时,使用Err对象的Raise方法,可在所有错误处理程序中重新生成错误,以解决处理程序中无针对某特定错误的代码的情况。这使得应用程序可试着沿调用列表在其他错误处理例程中更正错误。另外,它可确保在发生代码不能处理的错误时,Visual Basic 会显示错误信息。在进行代码测试时,该技巧可帮助你发现那些未被充分处理的错误。
  • 如果需要在完成错误处理后清除Err对象,使用Clear方法,这对于使用On Error Resume Next进行直接错误处理是有必要的。每当执行任意类型的 Resume 语句、Exit Sub、Exit Function、Exit Property或任何 On Error 语句时,Visual Basic 就会自动调用 Clear 方法。
  • 如果不打算用调用列表中的另一过程来捕获错误,使用Stop语句以强行中断程序代码。使用Stop可使你对错误的内容进行检查,同时在开发环境下对代码进行修改。
  • 编写一个失效保险(fail-safe)错误处理过程,以便代码中的所有错误处理程序可把该过程作为其解决不能处理的错误的最后方法。失效保险过程通过卸载窗体和保存数据,可有序地执行应用程序的中断。

生成错误以测试错误处理

在进行应用程序测试时或者想要将某个特定条件当作是一个Visual Basic 运行时错误时,模拟错误是非常有用的。例如,也许你正使用在某个外部应用程序中定义的对象编写一个模块,并想在应用程序的余下部分中,把从对象中返回的错误当作实际的Visual Basic 错误。

为了测试所有可能的错误,可能需要在代码中生成一些错误。可使用Err对象的Raise方法在代码中生成一个错误。 Raise方法中的一个变量列表可被Raise传递。当代码到达一个Resume语句时,自动调用Err 对象的Clear方法。为了将错误传递回调用堆栈的前一过程,有必要重新生成一个错误。

通过向错误提供错误代码,还可模拟任何Visual Basic 运行时错误。

自定义错误

有时,你可能想在Visual Basic 定义的错误外补充定义一些错误。例如,依靠调制解调器连接的应用程序在载波信号下降时可能会出现错误。如果想生成并捕获这个错误,你可将错误号赋给vbObjectError常量。

vbObjectError常量储存的数值间于其偏差值(Offset)和偏差值与512的总和之间。为确保自定义的错误号不与以后的Visual Basic 冲突,所用的数值高于该值。为自定义错误号,可在模块的申明部分添加常量。

' 错误常量Const gLostCarrier = 1 + vbObjectError + 512Const gNoDialTone = 2 + vbObjectError + 512

然后,你可使用Raise方法来生成任何内部错误。对于这种情况,Err 对象的描述属性将返回一个标准的描述“应用程序定义的错误或对象定义的错误”。要提供自定义错误的描述,你需要把它作为一个参数添加到Raise 方法中。

直接错误处理

若在可能导致错误的每一行后立即检测错误,这就是直接错误处理。使用直接错误处理,可以编写在错误发生时返回错误号的函数和语句;在过程中产生一个Visual Basic 错误,并在调用过程中处理错误;或者编写一个返回Variant数据变量的函数,为调用过程指示所发生的错误。

返回错误号

返回错误号的方法有很多,其中最简单的方法是创建一个在错误发生时返回错误号的函数和语句,而不是返回某个变量。下面示例说明了如何在FileExists函数示例中使用这种方法,以指示某特定文件是否存在。

Function FileExists (p As String) As LongIf Dir (p) <> " " ThenFileExists = conSuccess   ' 返回一个常量,指示文件存在。   ElseFileExists = conFailure  ' 返回一个常量,指示失败。   End IfEnd FunctionDim ResultValue As LongResultValue = FileExists ("C:\Testfile.txt")If ResultValue = conFailure Then..  ' 处理错误。   .Else..  ' 继续进行程序。   .End If

直接错误处理的关键在于在每个语句或函数调用后立即检测错误。在这种方式中,你可设计一个处理程序,以准确预计可能出现的错误的类别,并采用相应的方法解决错误。这种方法不需要产生一个实际的运行时错误。

在调用过程中处理错误

指示错误情况的另一方法是在过程自身中生成一个Visual Basic 错误,并在调用过程的直接错误处理程序中处理这个错误。下面的示例说明在FileExists过程中,当操作不成功时产生的错误号。在调用该函数前,On Error Resume Next语句在错误发生时为Err对象属性赋值,但并不企图运行一个错误处理例程。

On Error Resume Next语句后紧跟错误处理代码。这个代码可检测Err 对象的属性,以确定是否有错误发生。如果Err.Number不为零,则已发生错误,根据Err对象的属性值,错误处理代码可采取相应的措施。

Function FileExists (p As String)If Dir (p) <> " " ThenErr.Raise conSuccess      ' 返回一个常量,指示文件存在。   ElseErr.Raise conFailure    ' 产生错误号conFailure   End IfEnd Function Dim ResultValue As LongOn Error Resume NextResultValue = FileExists ("C:\Testfile.txt")If Err.Number = conFailure Then..  ' 处理错误。   .Else..  ' 继续进行程序。   .End If

下一示例同时使用返回变量和一个传递的自变量,以指示错误的情况。

Function Power (X As Long, P As Integer, ByRef Result As Integer) _As LongOn Error GoTo ErrorHandlerResult = x^PExit FunctionErrorHandler:Power = conFailureEnd Function ' 调用Power函数。Dim lngReturnValue As Long, lngErrorMaybe As LonglngErrorMaybe = Power (10, 2, lngReturnValue)If lngErrorMaybe Then..  ' 处理错误。   .Else .  ' 继续进行程序。   .End If

如果编写的函数只是简单地返回一个结果变量或返回一个错误代码,那么结果变量可能在错误代码的范围内,这样调用过程就无法区别是结果变量还是错误代码。返回变量和一个传递自变量的同时使用,可使你的程序确定函数调用是否失败,并采用适当的措施。

Variant数据类型的使用

返回内部错误信息的另一种方法是利用Visual Basic 的Variant数据类型及相关的一些函数。Variant具有一个标识符,可指示变量中包含数据的类型,并且Variant还可被标识为一个 Visual Basic 错误代码。你可以编写一个函数来返回一个Variant,并使用标识符为调用过程指示所发生的错误。

下面示例说明如何编写Power函数,以返回一个Variant。

Function Power (X As Long, P As Integer) As VariantOn Error GoTo ErrorHandlerPower = x^PExit FunctionErrorHandler:Power = CVErr(Err.Number) ' 将错误代码转换为标识的变量。End Function ' 调用Power函数。Dim varReturnValue As VariantvarReturnValue = Power (10, 2)If IsError (varReturnValue) Then..  ' 处理错误。   .Else..  ' 继续进行程序。   .End If

集中化错误处理

在为你的应用程序添加错误处理代码时,你很快会发现自己在一遍一遍地处理同样的错误。仔细制定计划,编写一些错误处理代码可调用的过程,以便处理共同错误情况,从而缩短代码。

在下面的FileErrors函数中,当错误发生时会显示一条适当的消息,若可能,将允许用户通过选择按钮来确定程序的下一步动作。然后,代码返回给调用函数的过程。代码值指示程序的下一步动作。注意用户自定义的常量(如MnErrDeviceUnavailable)必须在某处进行定义(全局常量或者包含该过程的模块的模块级常量或者该过程内部常量)。

Function FileErrors() As IntegerDim intMsgType As Integer, strMsg As StringDim intResponse As Integer' 返回值          含义   ' 0         Resume   ' 1         Resume Next   ' 2         Unrecoverable error(不可恢复的错误)   ' 3         Unrecognized error(不可识别的错误)   intMsgType = vbExclamationSelect Case Err.NumberCase MnErrDeviceUnavailable               ' 错误 68             strMsg = "驱动器无效。"intMsgType = vbExclamation + 4Case MnErrDiskNotReady                        ' 错误 71             strMsg = "在软驱中插入磁盘并关上软驱门。"Case MnErrDeviceIO                               ' 错误 57             strMsg = "内部磁盘错误。"intMsgType = vbExclamation + 4Case MnErrDiskFull                                  ' 错误 61         strMsg = "磁盘满,要继续吗?"intMsgType = vbExclamation + 3Case ErrBadFileName, ErrBadFileNameOrNumber   ' Error 64 & 52             strMsg = "文件名非法。"Case ErrPathDoesNotExist                 ' 错误76             strMsg = "路径不存在。"Case ErrBadFileMode                                ' 错误54             strMsg = "不能以该访问方式打开你的文件。"Case ErrFileAlreadyOpen                   ' 错误55             strMsg = "文件已被打开。"Case ErrInputPastEndOfFile                ' 错误 62             strMsg = "文件有一个非标准的文件结尾标志。 "strMsg = strMsg & "或者试图读取文件结尾标志后的内容。"Case ElseFileErrors = 3Exit FunctionEnd SelectintResponse = MsgBox (strMsg, strMmsgType, "磁盘错误")Select Case intRresponseCase 1, 4    ' OK(确定)和Retry(重试)按钮。             FileErrors = 0Case 5               ' Ignore(忽略)按钮。             FileErrors = 1Case 2, 3    ' Cancel(取消)和 End(结束)按钮。             FileErrors = 2Case ElseFileErrors = 3End SelectEnd Function

上面的函数可处理共同的文件和磁盘相关错误。如果错误与磁盘的输入/输出无关,函数将返回值3,那么调用这个过程的过程有两种处理错误的方法:一是自己处理错误,用Raise方法重新生成错误;二是调用另一过程来处理错误。

注:在你编写大型应用程序时,会发现在各个窗体和模块的多个过程中使用了相同的常量。将这些常量作为公用常量,并在单一的标准模块中作声明,这可以使代码得到更好的组织,并避免重复的输入相同的声明。

对于所有可能会出现读写磁盘错误的过程,使它们调用FileErrors过程可简化错误处理。

例如,你可能使用应用程序来警告试图替换一个已存在的磁盘文件。相反,当你试图打开一个不存在的文件时,应用程序会警告你文件不存在,并询问你是否要创建该文件。对于这两种情况,在应用程序将文件名传递给操作系统时都可能会发生错误。

关闭错误处理

如果一个错误陷井在某过程中有效,当该过程完成运行时,错误陷井会被自动关闭。然而,有时你可能想在过程代码仍处于运行时关闭错误陷井。要关闭一个有效的错误陷井,可使用On Error GoTo 0语句。在Visual Basic 运行这个语句后,错误可被检测到但并不在过程中捕获。在过程中的任何地方可使用On Error GoTo 0关闭错误处理,甚至在错误处理例程内也可以。

使用错误处理程序调试代码

在进行代码调试时,如果代码产生的错误被一个错误处理程序捕获,你可能会发觉对代码行为的分析是令人含混的。你可以在工程的每个模块中的On Error行后添加注释,但这种方法也很麻烦。

取而代之的是,在调试时关闭错误处理程序,这样一旦发生错误,程序就进入中断模式。

为此,在“选项”对话框(“工具”菜单)上的“通用”选项卡上选中“发生错误则中断”。若选中该选项,在工程中的任何地方发生错误,工程将进入中断模式,并在“监视”窗口中显示发生错误的代码。若未选中该选项,错误可能会也可能不会引起错误消息的显示,这取决于错误发生的位置。例如,错误可能是由应用程序所引用的外部对象引起的。如果显示提示消息,错误的起源将决定错误消息是否有意义。

处理引用对象中的错误

如果在过程中引用了一个或多个对象,要确定错误在何处发生就变得比较困难,尤其是错误发生在另一应用程序的对象中。例如,假设一个应用程序由一个窗体模块(MyForm)组成,MyForm引用一个类模块(MyClassA),类模块又引用了一个Microsoft Excel 工作表对象。

如果工作表对象不能处理工作表中产生的某个特定错误,它会重新生成一个错误,Visual Basic 将这个错误传递给引用工作表的对象MyClassA。Visual Basic 自动将未捕获的Visual Basic 外的对象中产生的错误映射为错误代码440。

MyClassA对象要么能处理错误(最好能)要么能重新生成一个错误。过程间的接口要求任何对象在重新生成一个引用对象中出现的错误时,不能只简单地传递错误(传递错误代码440),而应将错误号与具体含义映射起来。当你重新映射错误时,如果错误处理程序可确定该错误与定义的Visual Basic 错误相类似(例如,溢出或被零除等),那么错误号可以是表明错误情况的Visual Basic 定义数值,另外,错误号还可以是Visual Basic 未定义的错误号。在Visual Basic 常量vbObjectError中添加新数值,将对象产生的错误通知其他处理程序。

只要可能,应在一个类模块中尽力处理模块本身产生的所有错误,并还应尽力处理模块所引用的对象中产生的对象不能处理的错误。然而,由于一些错误是不能预计的,因此仍存在一些不能处理的错误。另外,有些情况下,在引用对象中处理错误比在被引用对象中处理错误更适宜。

当错误在窗体模块中发生时,Visual Basic 产生一个预先定义的Visual Basic 错误号。

注:如果你在创建一个公用类,要保证明确表述了你所定义的每个非Visual Basic 错误处理程序的含义。其他程序员在引用这个公用类时,需要知道如何处理你定义对象所产生的错误。

在重新生成一个错误时,保持Err对象的其他属性不变化。如果未捕获产生的错误,Source和Description属性将被显示出来,以帮助用户采取更正措施。

处理从引用对象传递的错误

一个类模块可包括下列错误处理程序,以解决任何可捕获的错误和重新生成不能解决的错误。

 MyServerHandler:Select Case ErrNumCase 7        ' 处理内存溢出错误                 ..Case 440    ' 处理外部对象错误             Err.Raise Number:=vbObjectError + 9999' 来自另一Visual Basic 对象的错误          Case Is > vbObjectError and Is < vbObjectError + 65536ObjectError = ErrNumSelect Case ObjectError' 对象根据为其提供的错误号处理错误             Case vbObjectError + 10..Case Else' 将错误与通用对象错误映射并重新生成错误                    Err.Raise Number:=vbObjectError + 9999End SelectCase Else' 将错误与通用对象错误映射并重新生成错误             Err.Raise Number:=vbObjectError + 9999End SelectErr.ClearResume Next

错误号440捕获那些在Visual Basic 应用程序外的引用对象中产生的错误。在本示例中,错误只是简单地用9999传递,因为这种集中化的处理程序很难确定错误的原因。发生这种错误通常是致命的自动化错误(会导致某组件结束运行)的结果,或者是由于对象不能正确处理一个捕获的错误。只有出现致命错误,错误号440才会被传递。如果象前面在“直接错误处理”中讨论的那样为一个内部处理程序编写错误陷井,则有可能确定错误的产生原因并更正错误。

语句Case Is > vbObjectError and Is < vbObjectError + 65536捕获发生在Visual Basic 应用程序内部对象的错误和捕获发生在包含处理程序的同一对象中的错误。只有被对象定义了的错误才处于vbObjectError偏差值范围内。

提供给对象的错误代码清单应定义所有可能的错误代码及其含义,这样,编写的处理程序就能灵活解决预计的错误。实际的错误代码可被设计为无vbObjectError偏差值的形式,或者在添加偏差值后编排错误代码,在这种情况下,Case Else语句应减去vbObjectError而不是加上偏差值。另一方面,对象错误可以是显示在对象类型库中的常量,如“对象浏览器”所示。在这种情况下,在Case Else语句中使用错误常量而不是错误代码。

任何未处理的错误应以一个新号重新生成,如Case Else语句所示。在编写的应用程序中,你可设计一个处理程序来预测你所定义的新号。如果是一个公用类,你还需要在应用程序文档说明中对新错误处理代码添加注释。

最后的Case Else语句捕获并重新生成在处理程序中未被捕获的任何错误。由于这部分的错误陷井会捕获添加或未添加vbObjectError常量的错误,因此,你应把这些错误与通用的“不可解决的错误”代码相映射。这个代码应添加到vbObjectError中,为任何处理程序指出发生在被引用对象中的错误。

调试被引用对象中的错误处理程序

当你在调试的应用程序引用了Visual Basic 创建的对象或引用了类模块中定义的类时,你可能会发现不易确定错误是由哪个对象产生的。为简化这个问题,你可选中“选项”对话框(“工具”菜单)上“通用”选项卡中的“在类模块中中断”。当选中该选项时,类模块中的错误可导致这个类进入调试的中断模式,使得你有机会对错误进行分析。