学习Excel技术,关注微信公众号:
excelperfect
主要内容:
- 理解运行时错误及其原因
- 如何在过程中启用错误捕获
- 使用Err对象
- 编写错误处理代码
- 延迟错误处理
- 使用错误作为编程工具
运行时错误是在程序运行时发生的错误,除非处理该错误,否则它可以使程序在运行时停止。本课讲解什么是运行时错误以及如何在程序中处理它们。
什么是运行时错误?
在程序运行时发生的程序错误称为运行时错误,重要的是要理解运行时错误(或只是错误)与在VBA程序中可能发生的其他两类问题之间的区别:
- bug是程序逻辑中的缺陷,会导致程序产生不正确的结果。与错误不同,bug不会阻止程序运行。
- 语法错误是VBA语法中的错误。VBA编辑器会在你编写代码时捕获并标记语法错误,因此它们永远不会影响程序执行。
当发生错误并且程序不包含处理错误的代码时,程序将停止并显示一个对话框,其中包含错误说明,如图26-1所示。通常无法从未处理的错误中恢复,这就是为什么它们如此讨厌的原因。
图26-1:VBA通过显示此对话框来发出未处理的错误信号
你会看到错误对话框提供了错误的简要说明以及错误号。每个VBA错误都有其自己的错误编号。在此示例中,错误是除数为零,该错误在代码尝试以零作为除数时发生,其编号为11。你还可以看到此对话框中有四个按钮,其中一个按钮被禁用。
- 继续。一些错误使程序可以继续执行,在这种情况下,此按钮处于启用状态。但是,大多数错误都不允许这样做。
- 结束。此按钮终止程序。
- 调试。如果程序在VBA编辑器中运行,则可以使用此按钮暂停程序并突出显示发生错误的代码行。但是,如果工程使用密码锁定(由于几乎总是锁定分发的应用程序),则“调试”选项将不可用。
- 帮助。此按钮显示有关发生的错误的信息,包括可能的原因。
你可以看到,对于最终用户,对未处理错误的唯一响应是结束程序。这个不好!实际上,没有任何理由。
错误原因
一些错误是由代码中的错误引起的。尝试访问不存在的数组元素是一个常见的示例。例如:
Dim MyArray(100) As Single
…
MyArray(150) = 1.2 ‘导致错误!
当你尝试使用尚未初始化的对象变量(即,该对象尚未实例化)时,会发生另一个与代码相关的常见错误。在此示例中,r是一个变量,可以包含对Range对象的引用,但是尚未初始化为引用实际单元格区域,因此第二行会导致错误。
Dim r As Range
r.Value = “Data”
其他错误是由硬件问题引起的。文件操作是导致错误的常见原因,例如,当程序尝试写入已满的磁盘或未插入任何介质时尝试写入可移动介质驱动器时。良好的编程习惯可以帮助防止由代码引起的错误,但是某些错误显然是程序员无法控制的。这是VBA程序应始终包含错误处理的原因之一。
错误和Excel对象模型
某些Excel对象内置了自己的错误处理。这意味着该对象在错误到达程序之前就将其拦截。一个示例是Workbook对象,如果尝试打开磁盘上不存在的工作簿,或者尝试将工作簿保存到不存在的磁盘上,则Workbook对象会捕获错误并显示其自己的对话框(如图26-2所示))。程序中的错误捕捉永远不会被激活。
不幸的是,Microsoft尚未提供有关对象内部捕获哪些错误以及将哪些错误传递给VBA进行处理的任何信息。可以做的只是做实验,看看哪些错误应被代码捕获,哪些错误可以安全地忽略,因为它们是由Excel对象处理的。
图26-2:一些Excel对象在内部处理错误并显示自已的对话框
避免错误
有一些好的编程习惯可以帮助避免程序中的错误,这些类似于第24课中建议的防止错误的实践,但是值得重复:
- 总是使用Option Explicit。通过要求变量声明,可以避免因变量名拼写错误而导致的许多错误。
- 避免使用Object数据类型和Variant数据类型来包含对象引用。将这些数据类型用于对象引用(而不是使用特定的对象类型)使VBA无法提前知道对象的方法和属性是什么,从而为各种错误埋下了隐患。
- 只要数组索引可能超出范围,在尝试给数组赋值或从数组中读取数据之前,使用LBound和UBound检查索引值。
- 总是验证用户输入的数据。错误的常见原因是用户输入不正确的数据,例如在需要数字时输入字符串。通过验证输入数据,你可以避免这种错误。
捕获错误
VBA中的错误是通过捕获它们来处理的。捕获错误时,告诉VBA:“发生错误时,不要显示默认对话框并暂停程序,而应将执行过程转到称为错误处理程序的特殊代码部分。”然后,错误处理程序中的代码处理错误。可以使用OnError Goto语句来捕获错误。语法为:
On Error Goto label
label是程序标签(名称后跟冒号),用于标识错误处理代码的位置。OnError Goto语句和错误处理代码必须始终在同一过程中。因此,带有错误处理的过程的结构如下所示:
Public Sub SomeProcedure()
'在这里声明变量.
On Error GoTo ErrorHandler
'这里是过程代码.
Exit Sub
ErrorHandler:
'这里是错误处理代码.
End Sub
关于此示例,有两点需要注意:
- 错误处理代码的label不必一定是ErrorHandler,它可以是任何合法的VBA标签名称。
- ExitSub语句用于防止执行陷入错误处理代码中,这样可以确保仅在发生错误时才执行错误处理代码。
在详细介绍编写错误处理代码之前,你需要了解Err对象,这将在下一部分中介绍。
提示:由于VBA过程的内容彼此独立,因此可以在多个过程中为错误处理代码使用相同的标签。
过程和错误处理
所有错误处理都在过程级别完成,这是不可避免的,因为错误捕获语句及其错误处理代码必须始终在同一过程中。这种安排之所以有效,是因为可以将给定过程中的错误处理代码调整为适合该过程中可能发生的特定错误。执行数学计算的过程应注意溢出和零除错误,但是该过程可以忽略与文件相关的错误,因为在该过程执行期间它们不会发生。
在VBA程序中,过程通常会调用其他过程。如果一个过程未启用错误捕获,则在执行过程中发生的任何错误将传递给调用它的过程。因此,如果Proc1调用Proc2,并且Proc2没有错误陷阱,则Proc2中的错误将传递给Proc1并在那里进行处理。如果Proc1没有错误陷阱,则错误将传递到下一个级别。只有当错误达到最高级别(该过程未被另一个过程调用)时,才会触发VBA的默认错误机制。
集中通过一个或几个过程进行一些错误处理可能很诱人,但建议你避免这种做法,并为每个过程赋予其自己的错误处理代码。
提示:错误处理对于所有VBA过程(包括函数和类方法)都以相同的方式工作。
Err对象
Err对象是VBA不可或缺的一部分,总是可用于你的程序。任何时候,Err对象都包含有关最近发生的错误的信息。Err对象具有以下属性和方法:
- Number。最新的错误号,如果没有发生错误,则为0。
- Description。最新错误的简短描述;如果未发生错误,则为空白。
- Clear。从对象中清除错误信息。
发生错误时,使用Err对象获取有关错误的信息。Number属性标识错误,Description属性提供可以在需要时显示给用户的信息。调用Clear方法,以确保Err对象不会保留来自先前错误的信息。
提示:Err对象的Description属性提供的错误描述与VBA的默认错误对话框中显示的错误描述相同。此外,Number是Err对象的默认属性。因此,If Err= 0等效于IfErr.Number = 0。
错误处理代码
错误处理代码的任务是
1.识别错误(使用Err对象)。
2.采取适当的操作,例如向用户显示消息。
3.恢复程序执行。
为了识别错误,你必须对在此特定过程中可能发生的错误有所了解。然后,针对这些潜在错误中的每一个,测试Err.Number属性。找到匹配项后,采取适合该错误的操作。例如,假设你的程序正在尝试打开位于共享网络驱动器上的工作簿文件,可能会发生几种错误:
- 错误53,找不到文件
- 错误76,找不到路径
- 错误68,设备不可用
- 错误75,路径/文件访问错误
根据发生的错误,可能希望提示用户重试,联系网络管理员或采取其他操作。处理错误没有万灵丹。你需要了解可能发生的错误,可以采取哪些措施(如果有的话)来纠正它们,以及该错误在程序上下文中的含义。
对于某些错误,除了通知用户然后结束程序外,别无选择,但是对于许多错误,可以恢复该程序。这意味着错误处理代码的一部分是恢复程序执行的指令。共有三个选项:
- Resume。通过重试导致错误的语句来恢复执行。错误可能已得到纠正(例如在A:驱动器中插入软盘)后,使用Resume。
- Resume Next。继续执行导致错误的语句后的语句。当无法修复错误条件但仍可以执行其余过程代码时,使用ResumeNext。
- Resume label。使用标签标识的语句(必须在同一过程中)恢复执行。当Resume和Resume Next都不适合时,使用Resume label语句。
提示:执行任何Resume语句会自动清除Err对象中现有的错误信息,就像已调用Err.Clear方法一样。每当执行退出过程时,Err对象也会被清除。
延迟错误处理
另一种错误处理技术是延迟对错误的处理。换句话说,VBA不会捕获错误,而是将其忽略。然后,你的代码可以检查Err对象,以查看是否发生的错误类型。若要延迟错误处理,使用OnError Resume Next语句。任何后续的错误(在该过程中)都将被忽略,有关该错误的信息将放置在Err对象中。你的代码可以使用此信息来确定是否发生错误,以及发生错误采取的操作。你将按照以下方式构造代码:
On Error Resume Next
‘这里放置可能导致错误的代码.
‘如果没有发生错误.Err.Number是0.
If Err.Number > 0 Then
‘这里是检查错误号的代码及合适的响应.
End If
这项技术只能用于某些错误,特别是那些可以推迟处理的错误。该技术不适用于即使是暂时也无法忽略的错误。无法准确定义属于此类别的错误,严重的错误必须在一个程序中捕获,而在另一个程序中可能会被接受。同样,程序员有责任熟悉可能发生的错误,以及它们如何与特定程序相关联。
注意:当On Error ResumeNext生效时,你不能使用任何Resume语句来响应错误。仅在与OnError Goto语句关联的错误处理程序中才允许使用Resume语句。
一些错误处理示例
在处理VBA错误处理时,有一些经验是很有价值的。为了向你提供一些处理错误的经验,本节提供了一些使用VBA的错误处理功能的示例。
忽略错误
Excel的SpecialCells方法使你能够获得对满足特定条件的区域内的单元格的引用,例如包含公式的单元格或包含批注的单元格。如果找不到匹配的单元格,则会产生错误。在许多情况下,此错误可以简单地忽略它,因为它不会影响程序操作。
清单26-1展示了一个过程,该过程选择当前所选区域中包含批注的所有单元格。如果选择的区域中没有批注单元格,则使用On ErrorResume Next语句忽略结果错误,并且选择内容不变。如果没有错误,则选择包含批注的单元格。
清单26-1:使用On Error Resume Next忽略错误
Public Sub SelectCellsWithComments()
Dim r As Range
On Error Resume Next
Set r = Selection.SpecialCells(xlCellTypeComments)
If Err = 0 Then r.Select
End Sub
通知用户错误
继续上一节中的示例,如果该过程未找到任何包含批注的单元格,则可能需要通知用户。可以修改代码以使用OnError Goto提供此类通知,如清单26-2所示。
清单26-2:使用On Error Goto通知用户错误
Public Sub SelectCellsWithComments()
Dim r As Range
On Error GoTo ErrorHandler
Set r = Selection.SpecialCells(xlCellTypeComments)
Exit Sub
ErrorHandler:
MsgBox "在所选区域中没有找到有批注的单元格."
End Sub
使用错误作为编程工具
在某些情况下,VBA的错误捕获功能可以用作编程工具。错误可以提醒你存在某种情况,并且你的程序可以采取相应的操作。
作为示例,考虑以下代码:
Dim wb As Workbook
Set wb = Workbooks(“SalesData”)
如果打开名为SalesData的工作簿,则此代码可以正常工作。但是,如果没有打开,则会发生错误。程序可以在无法提前知道是否打开特定工作簿的情况下使用此功能。清单26-3展示了一个函数,该函数在打开时返回对工作簿的引用,或者在没有打开时返回Nothing。程序可以调用此函数并测试其返回值。如果此值为Nothing,则程序可以采取步骤打开工作簿。
清单26-3:使用错误作为编程工具
Public Function GetReferenceToWorkbook(wbName As String) As Workbook
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(wbName)
If Err = 0 Then 'wbName被打开
Set GetReferenceToWorkbook = wb
Else
Set GetReferenceToWorkbook = Nothing
End If
End Function
要点回顾
在本课程中,你学习了如何使用VBA的错误处理工具来处理运行时错误。
- 未处理的错误将使程序停止运行,并可能导致数据丢失。
- 可以通过执行OnError语句之一来启用VBA的错误捕获功能。
- 所有VBA错误处理均在过程级别进行。
- 发生错误时,Err对象将提供有关该错误的信息。
- 可以使用On Error ResumeNext语句推迟错误处理。
- 错误有时可以用作编程工具。
自我测评
1.什么时候可以接受发生错误而不进行处理?
2.说明是否所有可能的错误原因都在程序员的控制之下。
3.是否可以在一个过程中放置OnError Goto label语句,并在另一个过程中放置错误处理代码?
4.没有错误发生时,Err.Number属性的值是什么?
5.在错误处理代码中,如何使用引起错误的语句恢复程序执行?
注:本文是在知识星球App的完美Excel社群中发表的Excel VBA编程系列文章中的一篇,翻译整理自《Excel Programming Weekend Crash Course》。这些文章正陆续更新中。
欢迎在下面留言,完善本文内容,让更多的人学到更完美的知识。