错误处理的分层结构

激活的错误处理程序通过执行 On Error 语句而被激活,并且不会被 On Error GoTo 0 语句关闭,也不会通过退出激活它的过程而关闭。活动的错误处理程序是当前正在执行的错误处理程序。为了使错误处理程序为活动的,首先应激活它,但并非所有已激活的错误处理程序都是活动的。例如,在 Resume 语句之后,处理程序失活但仍然是激活的。

当错误发生在没有激活的错误处理例程过程内部,或发生在活动的错误处理例程内部时,Visual Basic 对另一个激活的错误处理例程搜索调用列表。调用列表是调用的序列,该序列指向当前执行的过程;它被显示在 Call Stack 对话框中。只有在中断方式下(在暂停执行应用程序时),选定“视图”、“字体”菜单项或按 CTRL+L 键,才能显示“字体”对话框。

搜索调用列表

假设以下调用序列发生,如图 13.2 所示:

  1. 一事件过程调用 Procedure A。

  2. Procedure A 调用 Procedure B。

  3. Procedure B 调用 Procedure C。

图 13. 2 调用序列

当执行 Procedure C 时,其它过程挂起,如“字体”对话框中调用列表所示。

详细信息 详细信息,请参阅本章后面“监视调用堆栈”中的内容。

图 13.3 显示了“字体”对话框中的调用列表。

图 13.3 当过程挂起时的调用列表

如果 Procedure C 中出现错误,而且该过程没有激活的错误处理程序,则 Visual Basic 在调用列表中沿挂起的过程向后搜索,首先搜索 Procedure B、 Procedure A,然后是初始事件过程(但非父过程),并执行查找到的第一个激活的错误处理程序。如果在调用列表中的所有地方都没有遇到激活的错误处理程序,则显示默认的意外错误信息并终止执行。

如果 Visual Basic 查找到一个激活的错误处理例程,那么,就好象在包含错误处理程序的同一过程中出现了错误那样,执行将在该例程中继续下去。如果在错误处理例程中执行 Resume 或 Resume Next 语句,则如下表所示,执行也见继续下去。

语句 结果
Resume 再次执行这样一个过程的调用,该过程是Visual Basic 搜索的过程。在前述的调用列表中,如果 Procedure A 有激活的包含 Resume 语句的错误处理程序,则 Visual Basic 对 Procedure B 再执行调用。
Resume Next 执行将返回到过程中已执行过的上一条语句之后的那条语句。这就是紧随着调用过程的语句,而过程又恰好是 Visual Basic 后退搜索过的。在前述调用列表中,如果 Procedure A 有激活的包含 Resume Next 语句的错误处理程序,则执行将返回到调用 Procedure B 之后的语句。

注意,已执行的语句在找到错误处理过程的过程中,而不必在错误发生的过程中。如果不考虑这一点,则代码可能不尽如人意。为使代码更易于调试,无论何时出现错误都可直接了当进入中断方式,如同本章后面的“关闭错误处理”一节解释的那样。

如果错误处理程序的错误范围不包括实际产生的错误,则在具有激活的错误处理程序的过程内会产生意想不到的错误。此时,过程可能会无止境地执行下去,特别是如果错误处理程序还执行 Resume 语句的话,情况更是如此。为防止这种情况出现,在程序内的 Case Else 语句中使用 Err 对象的 Raise 方法。这实际上是在错误处理程序内生成一错误,从而迫使 Visual Basic 通过调用列表搜索能够处理错误的处理程序。

在 Errors.vbp 示例应用程序的 VerifyFile 过程示例中,将原来包含在 Err.Number 中的数赋予变量 intErrNum,在 Case Else 语句中,该变量将随后作为参数传递到 Err 对象的 Raise 方法中,由此而生成错误。当这样的错误在活动的错误处理程序内产生时,应着手通过调用列表后退搜索。

将错误分配到不同的处理程序

通过调用列表后退搜索的效果很难预测,因为这要取决于在成功地处理错误的处理程序中是否执行了 Resume 或 Resume Next。Resume 将控件返回到刚执行过的过程调用,而该过程包含错误处理程序。Resume Next 则将控件返回到最近一次执行过的过程调用之后的任何语句之处,而且调用的过程包含错误处理程序。

例如,在图 13.3 所示的调用列表中,如果 Procedure A 含有激活的错误处理程序,而 Procedure B 和 C 未包含这样的程序,则在 Procedure C 中发生的错误将由 Procedure A 中的错误处理程序处理。如果一退出,该错误处理程序就使用 Resume 语句,则程序继续调用 Procedure B。但是,如果一退出,Procedure A 的错误处理程序就使用 Resume Next 语句,则在 Procedure A 中,不管 Procedure B 的调用之后是什么语句,程序都将继续执行该语句。在这两种情况下,错误处理程序都不直接返回到最初发生错误的过程或语句。

复杂错误处理指南

当用多个模块编写大型 Visual Basic 应用程序时,错误处理代码可能相当复杂。请牢记这些方针:

详细信息 请参阅本章后面的“联机错误处理”、“设计时、运行时以及中断模式”及“通过生成错误测试错误处理”。