ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 20338|回复: 22

[VBA程序开发] [转帖]Visual Basic的调试和错误处理

[复制链接]

TA的精华主题

TA的得分主题

发表于 2005-1-22 00:42 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:数据类型和基本语句

内容:

如何处理错误 错误处理程序的设计

错误处理层次结构 生成错误以检测错误处理

直接(Iinline)错误处理 集中化错误处理

关闭错误处理 处理引用对象中的错误

调试的方法 避免产生错误(Bug)

设计时、运行时和中断模式 调试窗口的使用

中断模式的使用 运行应用程序的选定部分

监视调用堆栈 使用“立即”窗口测试数据和过程

调试的特殊考虑 调试的提示

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

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

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

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

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

[此贴子已经被作者于2005-1-22 0:55:25编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:42 | 显示全部楼层

如何处理错误

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

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

Function FileExists (filename) As Boolean

FileExists = (Dir(filename) <> "")

End Function

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

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

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

Function FileExists (filename) As Boolean

Dim Msg As String

' 打开错误陷井,这样错误处理程序可对检测到的任何错误作出响应。

On Error GoTo CheckError

FileExists = (Dir(filename) <> "")

' 如果没有错误发生,不执行错误处理程序。

Exit Function

CheckError:' 如果发生错误,转移至此。

' 定义代表Visual Basic 内部错误代码的常量。

Const mnErrDiskNotReady = 71, mnErrDeviceUnavailable = 68

' vbExclamation, vbOK, vbCancel, vbCritical, and vbOKCancel 是

' VBA类型库中定义的常量。

If (Err.Number = MnErrDiskNotReady) Then

Msg = " 在软驱中插入软盘并关上软驱门。"

' 显示一个带有感叹号符和OK、Cancel按钮的消息框。

If MsgBox(Msg, vbExclamation & vbOKCancel) = vbOK Then

Resume

Else

Resume Next

End If

ElseIf Err.Number = MnErrDeviceUnavailable Then

Msg = "该驱动器或路径中不存在:" & filename

MsgBox Msg, vbExclamation

Resume Next

Else

Msg = "意外错误#" & Str(Err.Number) & " 发生: " _ & Err.Description

'显示带有Stop(停止)符图标和OK按钮的消息框。

MsgBox Msg, vbCritical

Stop

End If

Resume

End Function

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

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

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

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

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

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

[此贴子已经被作者于2005-1-22 0:45:31编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:43 | 显示全部楼层

错误处理程序的设计

错误处理程序是在你编写的应用程序中捕获错误和作出响应的例程。你应在你认为可能出现错误的过程中添加错误处理程序(除非你可明确判定,否则应假定任何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 Variant

Const mnErrDivByZero = 11, mnErrOverFlow = 6, mnErrBadCall = 5

On Error GoTo MathHandler

Divide = numer / denom

Exit Function

MathHandler:

If Err.Number = MnErrDivByZero Or Err.Number = ErrOverFlow _

Or Err = ErrBadCall Then

Divide = Null '如果发生被零除、溢出或非法过程调用错误,返回Null。

Else

' 显示意外错误信息

MsgBox "意外错误 " & Err.Number & ": " & _

Err.Description, vbExclamation

End If ' 对于所有情况都继续执行Resume Next。

Resume Next ' 从Exit Function语句执行。

End Function

在特定行恢复执行

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

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

Function VerifyFile As String

Const mnErrBadFileName = 52, mnErrDriveDoorOpen = 71

Const mnErrDeviceUnavailable = 68, mnErrInvalidFileName = 64

Dim strPrompt As String, strMsg As String, strFileSpec As String

strPrompt = "输入检查的文件名称:"

StartHere:

strFileSpec = "*.*" ' 以一默认的名称开始。

strMsg = strMsg & vbCRLF & strPrompt

'让用户更改默认名称。

strFileSpec = InputBox(strMsg, "文件查找", strFileSpec, 100, _100)

' 如果用户删除默认名称,退出。

If strFileSpec = "" Then Exit Function

On Error GoTo Handler

VerifyFile = Dir(FileSpec)

Exit Function

Handler:

Select Case Err.Number ' 分析错误代码并加载信息。

Case ErrInvalidFileName, ErrBadFileName

strMsg = "你输入的文件名称无效,请重试一次。"

Case MnErrDriveDoorOpen

strMsg = "关上磁盘驱动器门,重试一次。"

Case MnErrDeviceUnavailable

strMsg = "你所指定的驱动器未找到,重试一次。"

Case Else

Dim intErrNum As Integer

intErrNum = Err.Number

Err.Clear ' 清除Err对象。

Err.Raise Number:= intErrNum ' 重新生成错误。

End Select

Resume StartHere '跳转回StartHere标签,使用户可重新输入另一文件名。

End Function

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

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

[此贴子已经被作者于2005-1-22 0:46:41编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:43 | 显示全部楼层

错误处理层次结构

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

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

调用列表的搜索

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

某事件过程调用过程A。

过程A调用过程B。

过程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 + 512

Const gNoDialTone = 2 + vbObjectError + 512

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

直接错误处理

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

返回错误号

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

Function FileExists (p As String) As Long

If Dir (p) <> " " Then

FileExists = conSuccess ' 返回一个常量,指示文件存在。

Else

FileExists = conFailure ' 返回一个常量,指示失败。

End If

End Function

Dim ResultValue As Long

ResultValue = 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) <> " " Then

Err.Raise conSuccess ' 返回一个常量,指示文件存在。

Else

Err.Raise conFailure ' 产生错误号conFailure

End If

End Function

Dim ResultValue As Long

On Error Resume Next

ResultValue = 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 Long

On Error GoTo ErrorHandler

Result = x^P

Exit Function

ErrorHandler:

Power = conFailure

End Function

' 调用Power函数。

Dim lngReturnValue As Long, lngErrorMaybe As Long

lngErrorMaybe = 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 Variant

On Error GoTo ErrorHandler

Power = x^P

Exit Function

ErrorHandler:

Power = CVErr(Err.Number) ' 将错误代码转换为标识的变量。

End Function

' 调用Power函数。

Dim varReturnValue As Variant

varReturnValue = Power (10, 2)

If IsError (varReturnValue) Then

.

. ' 处理错误。

.

Else

.

. ' 继续进行程序。

.

End If

[此贴子已经被作者于2005-1-22 0:47:19编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:43 | 显示全部楼层

集中化错误处理

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

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

Function FileErrors As Integer

Dim intMsgType As Integer, strMsg As String

Dim intResponse As Integer

' 返回值 含义

' 0 Resume

' 1 Resume Next

' 2 Unrecoverable error(不可恢复的错误)

' 3 Unrecognized error(不可识别的错误)

intMsgType = vbExclamation

Select Case Err.Number

Case MnErrDeviceUnavailable ' 错误 68

strMsg = "驱动器无效。"

intMsgType = vbExclamation + 4

Case MnErrDiskNotReady ' 错误 71

strMsg = "在软驱中插入磁盘并关上软驱门。"

Case MnErrDeviceIO ' 错误 57

strMsg = "内部磁盘错误。"

intMsgType = vbExclamation + 4

Case MnErrDiskFull ' 错误 61

strMsg = "磁盘满,要继续吗?"

intMsgType = vbExclamation + 3

Case 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 Else

FileErrors = 3

Exit Function

End Select

intResponse = MsgBox (strMsg, strMmsgType, "磁盘错误")

Select Case intRresponse

Case 1, 4 ' OK(确定)和Retry(重试)按钮。

FileErrors = 0

Case 5 ' Ignore(忽略)按钮。

FileErrors = 1

Case 2, 3 ' Cancel(取消)和 End(结束)按钮。

FileErrors = 2

Case Else

FileErrors = 3

End Select

End 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 ErrNum

Case 7 ' 处理内存溢出错误

..

Case 440 ' 处理外部对象错误

Err.Raise Number:=vbObjectError + 9999

' 来自另一Visual Basic 对象的错误

Case Is > vbObjectError and Is < vbObjectError + 65536

ObjectError = ErrNum

Select Case ObjectError

' 对象根据为其提供的错误号处理错误

Case vbObjectError + 10

..

Case Else

' 将错误与通用对象错误映射并重新生成错误

Err.Raise Number:=vbObjectError + 9999

End Select

Case Else

' 将错误与通用对象错误映射并重新生成错误

Err.Raise Number:=vbObjectError + 9999

End Select

Err.Clear

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

[此贴子已经被作者于2005-1-22 0:47:55编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:44 | 显示全部楼层

调试的方法

本章中讲述的调试技巧使用的是Visual Basic 提供的分析工具。Visual Basic 不能为你诊断或修正错误,但它可以提供工具,帮助你分析程序如何从过程的一部分执行到另一部分以及变量和属性值在语句运行时如何变化等。调试工具使你能查看应用程序的内部,帮助你确定发生的情况和原因。

Visual Basic 调试工具包括断点、中断表达式、监视表达式、逐语句或逐过程以及显示变量和属性的值。Visual Basic 还包括特殊的调试特征,如编辑-继续能力、设置下一运行语句和在应用程序处于中断模式时测试过程。

错误类型

为理解调试的重要性,应考虑下面三种你可能遇到的错误,具体描述如下:

编译错误:这是由不正确的代码编写引起的。如果你在设计时输入的关键字不正确,遗漏了一些必要的标点或者使用Next语句而没有与之对应的For 语句等,Visual Basic 会在编译代码时检测出这些错误。

运行时错误:在应用程序运行过程中,如果一个语句试图进行不可能执行的操作,会发生这类错误(并由Visual Basic 检测到)。这类错误的例子有被零除等。

逻辑错误:当应用程序未按预计的方式执行时发生这类错误。一个应用程序具有句法有效的代码,运行时未执行任何非法操作,但产生的结果不正确。只有测试应用程序并分析结果,你才能保证应用程序的运行是正确的。

调试工具如何提供帮助

调试工具被设计成可帮助你进行逻辑错误和运行时错误的排故,并帮助你观察没有错误的代码的运行方式。

例如,在一长串的计算后可能产生不正确的结果。调试中的任务是确定何事、何处出了问题。也许你忘记初始化一个变量、也许是你选择了一个错误的运算或者使用了一个不正确的公式。

调试中并不存在“魔术棍”,也不存在固定的工作步骤。最基本的一点是,调试帮助你了解应用程序正在进行的运行情况。调试工具为你提供了应用程序当前状态的快速浏览,包括变量、表达式和属性的值以及活动的过程调用名。你对应用程序的工作理解得越透彻,也就能更快找到错误(Bug)。

在大多调试工具中,Visual Basic 在“调试”工具栏上提供了一些有用的按钮,其说明如下。

下表简要描述了每个工具的用途。本章讨论的是这些工具如何帮助你更有效地调试或分析应用程序。

调试工具

用途

运行/继续

从设计时切换到运行时(运行)或从中断模式切换到运行时(继续)。(在中断模式中,按钮的名称变为“继续”)。

中断

从运行时切换到中断模式。

重新设置

从中断模式或运行时切换到设计时。

运行到光标处

在模块中定义一行,Visual Basic 在此行暂停应用程序的执行。

逐语句

在应用程序中运行下一可执行代码行,并转入过程。

逐过程

在应用程序中运行下一可执行代码行,但不转入过程。

跳出

运行当前过程的余下部分,并在调用过程的下一行中断。

“本地”窗口

显示当前过程中变量的值。

“立即”窗口

当应用程序处于中断模式时,允许你运行代码或查询变量值。

“监视”窗口

显示选定表达式的值。

快速监视

当应用程序处于中断模式时,列出表达式的当前值。

调用堆栈

在中断模式中,显示一个对话框,其中列出所有被调用且未完成运行的过程。

避免产生错误(Bug)

在应用程序中,可采用下列方法避免产生错误:

仔细设计应用程序,记下相关事件以及代码对每一事件的响应方式。为每个事件过程和每个通用过程指定一个特定明确的目的。

加入大量的注释。在你返回并分析代码时,如果你已在注释中对每个过程的目的进行了说明,那么你就可以更好地理解代码。

在可能之处明显地引用对象。按照“对象浏览器”中的“类”对话框中列出的对象声明对象,而不是使用一个Variant或者通用对象数据类型。

编写一个应用程序中变量和对象的命名表,保证命名的一致性。

最易引起错误产生的一个因素是输入不正确的变量名或将一个控件与另一控件混淆。你可使用Option Explicit来避免把变量名拼错。

[此贴子已经被作者于2005-1-22 0:48:29编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:44 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助

设计时、运行时和中断模式

为了测试和调试一个应用程序,你需要理解在任何给定时间,程序处于这三种模式中的哪一种。在设计时使用Visual Basic 可创建一个应用程序,而在运行时是运行应用程序。在中断模式下,程序的执行被暂停,使你能够检测和修改数据。Visual Basic 的标题栏总是为你列出当前的模式。

下表中列出了这三种模式的特征以及在这三种模式间转换的技巧。

模式

描述

设计时

创建应用程序的大部分工作是在设计时完成的。你可设计窗体、图形控件、编写代码和使用“属性”窗口设置或查看属性值。你除了设置断点和创建监视表达式外,不能运行代码或使用调试工具。

要切换到运行时,单击“运行”按钮。要切换到中断模式,单击“运行”菜单上的“逐语句”;应用程序在第一个可执行语句进入中断模式。

运行时

在应用程序控制下,你可象用户那样与应用程序间进行交互。你可查看代码,,但不能修改代码。

要切换回设计时,单击“重新设置”按钮。要切换到中断模式,单击“中断”按钮。

中断模式

在运行应用程序时暂停执行。你可在同一点查看和编辑代码,检测或修改数据,重新启动应用程序,结束运行或继续运行。

要切换到运行时,单击“继续”按钮(在中断模式中,“运行”按钮变成了“继续”按钮)。要切换到设计时,单击“重新设置”按钮。

你可在设计时设置断点和监视表达式,但其他调试工具仅在中断模式下工作。参阅本章的“中断模式的使用”。

调试窗口的使用

有时,你可通过运行部分代码来找到问题的原因。然而,更常发生的情况是,你还需要分析数据发生了什么。你可以把问题隔离到某个具有不正确值的变量或属性,然后再确定变量或属性是怎样被赋予了不正确的值及其发生的原因。

利用调试窗口,你可在应用程序逐语句运行时,监视表达式和变量的值。共有三种调试窗口:“立即”窗口、“监视”窗口和“本地”窗口。单击“视图”菜单上的相应命令或者单击“调试”工具栏上的相应按钮,可显示这些窗口。

“立即”窗口显示代码中语句的调试结果信息,或者显示你直接输入窗口的请求。

“监视”窗口显示当前的监视表达式,这些表达式是你决定在代码运行时进行监视其值的表达式。一个中断表达式是一种特殊的监视表达式,它在定义的某个条件变为真时,引起Visual Basic 进入中断模式。在“监视”窗口中,“上下文”栏表明被监视的表达式所在的过程和模块。“监视”窗口只有在当前语句处于指定的上下文中时方可显示监视表达式的值。否则,“值”栏将显示一个消息,表明语句未在上下文中。

“本地”窗口显示当前过程范围内任何变量的值。当执行由一个过程切换到另一过程时“本地”窗口的内容将转变为仅反应适用于当前过程的变量。

提示:代表对象的变量在“本地”窗口中显示时,其名称左侧带上一个加号(+)。你可单击加号来扩展变量,显示对象的属性及其当前的值。如果对象的某个属性包含另一对象,它也可进行扩展。这同样适用于包含数组或用户自定义类型的变量。

[此贴子已经被作者于2005-1-22 0:49:05编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:44 | 显示全部楼层

中断模式的使用

在设计时中,你可改变应用程序的设计或代码,但不能观察所作改变对应用程序运行的影响。在运行时中,你可监视应用程序的行为,但不能直接修改代码。

中断模式暂停应用程序的执行,并使你能在任何时候快速查看应用程序的状态。变量和属性值得到保存,使你能分析应用程序的当前状态和进行可影响应用程序运行方式的修改。当一个应用程序处于中断模式时,你可完成下列工作:

修改应用程序中的代码;

观察应用程序的接口条件;

确定已被调用的活动过程;

监视变量、属性和语句的值;

查看或控制应用程序运行的下一个语句;

立即运行Visual Basic 语句;

手工控制应用程序的运行。

注:你可在设计时设置断点和监视表达式,但其他调试工具仅在中断模式下工作。

在出错语句进入中断模式

调试时,你可能想让应用程序在代码中你认为可能开始发生问题的起始之处暂停。这是Visual Basic 提供断点和Stop语句的原因之一。断点通过定义语句或一系列条件,使VB 自动停止执行,并使应用程序不运行包含断点的语句而进入中断模式。

在运行应用程序时,你可采用下列方法之一手工进入中断模式:

按下CTRL+BREAK键;

从“运行”菜单上选择“中断”;

单击工具栏上的“中断”按钮。

在应用程序闲时(应用程序间于事件处理进程之间)也可能中断执行。当这种情况发生时,不会在某特定行停止执行,但Visual Basic 切换到中断模式。

在发生下列任何情况时自动进入中断模式:

一个语句生成一个不可捕获的运行时错误;

一个语句生成一个运行时错误并且“选项”对话框(“工具”菜单)的“通用”选项卡上的“发生错误则中断”选项被选中;

在“添加监视”对话框中定义的中断表达式发生改变或变成“真”(这取决于你对中断表达式的定义);

执行到含有断点的行;

执行到一个Stop语句。

纠正运行时错误并继续执行

一些运行时错误是由输入代码时的简单疏忽引起的;这些错误容易被纠正。这方面的错误通常包括名称拼写错误和对象的属性或方法不匹配等。

通常,你能够在应用程序暂停行输入一个正确的代码,甚至可修改一些代码,然后继续程序的执行。可方便地从“运行”菜单上选择“继续”或单击工具栏上的“继续”按钮。当继续运行应用程序时,你可证实问题是否已被纠正。

如果你选中“发生错误则中断”选项,Visual Basic 使代码中的错误处理程序无效,这样,当一个语句生成一个运行时错误时,Visual Basic 进入中断模式。如果你未选中“发生错误则中断”选项,并且如果存在一个错误处理程序,那么它会插入代码并采取纠正措施。

一些变化(最经常的情况是改变变量声明或添加新变量或过程)需要你重新启动应用程序。当这种情况发生时,Visual Basic 显示一个消息,询问你是否要重新启动应用程序。

使用监视表达式监控数据

在进行应用程序调试时,某个运算可能不会得到你所期望的结果,或者当某个变量或属性接收某个特定的变量值或变量值范围时发生错误。许多调试问题不能立即跟踪到一个语句,因此你可能需要在一个过程的整个范围中观察某个变量或表达式的行为。

Visual Basic 自动为你监控你所定义的监视表达式。在应用程序进入中断模式时,这些监视表达式出现在“监视”窗口中,从而使你可观察这些表达式的行为。

你还可指定监视表达式,使表达式的值发生改变或等于某个特定的值时,应用程序进入中断模式。例如,要取代循环中数十次或数百次的逐语句执行,你可使用一个监视表达式,使应用程序在循环指针达到某个特定值时进入中断模式。或者你可使应用程序在过程中的某个标记每次变化时进入中断模式。

添加、编辑或删除监视表达式

你可在设计时或中断模式中进行监视表达式的添加、编辑或删除。使用“添加监视”对话框(“调试”菜单)进行监视表达式的添加。

使用“编辑监视”对话框(“调试”菜单)可修改或删除一个已存在的监视表达式。“编辑监视”和“添加监视”对话框上的组件基本相同(除“删除”按钮只在“编辑监视”对话框上出现)。下表描述了两个对话框的相同部分:

组件

描述

“表达式”框

用来输入监视表达式。此表达式可为一个变量、属性、函数调用或其他任何有效的表达式。当显示“添加监视”对话框时,“表达式”框中包含当前的表达式(若有)

“上下文”选项组

设置表达式中所监视的变量范围。在相同名称的变量有不同的范围时使用。还可以将监视表达式中变量的范围限定为某个特定过程或某个特定窗体或模块,或者选中“所有过程”和“所有模块”,使变量应用于整个应用程序。对于上下文范围窄的情况,Visual Basic 可更快地评估变量。

“监视类型”选项组

设置Visual Basic 如何响应监视表达式。在应用程序进入中断模式时,Visual Basic 可监视这个表达式,并在“监视”窗口显示表达式的值。或者当监视表达式的值为真(非零)或表达式的值每次改变时,使应用程序自动进入中断模式。

提示:你可以将程序窗口中选定的表达式,拖到“监视”窗口中。

快速监视的使用

在中断模式中,你可检查那些为被定义为监视表达式的属性、变量或表达式。使用“快速监视”对话框(“调试”菜单或工具栏),可检查这些表达式。“快速监视”对话框显示模块中选定的表达式的值。单击“添加”按钮,可继续监视这个表达式;在“监视”窗口中,与“快速监视”对话框中的输入相关的信息将被显示。如果Visual Basic 不能评估当前表达式的值,“添加”按钮无效。

使用断点有选择地暂停执行

在运行时中,一个断点使Visual Basic 在执行某个特定的代码行前暂停。当Visual Basic 正在执行一个过程并遇到含有断点的代码行时,将切换到中断模式。

你可在中断模式、设计时或者应用程序闲的运行时设置或清除一个断点。单击靠近代码行的边界标识条(模块窗口的左边界条),可设置或清除一个断点。在设置断点时,Visual Basic 使用在“选项”对话框(“工具”菜单)的“编辑器格式”选项卡上指定的颜色对选定的代码行反亮加粗。

在一个模块中,Visual Basic 指明断点的方式是以指定的断点颜色系统填充和加粗代码行文字。在当前语句或要运行的下一语句四周有一个反亮的方框。对于当前语句包含一个断点的情况,仅在代码行四周有一个反亮的轮廓。在当前语句移到另一行时,含断点的行被显示成粗体并按指定的颜色系统显示。下面示例说明了一个在第四行含有断点的过程。

在到达一个断点和应用程序暂停时,你可以检测应用程序的当前状态。检查应用程序的结果是比较容易的,这是因为你可以在应用程序的窗体、模块以及调试窗口之间移动焦点。

一个断点可使应用程序恰好在执行含有该断点的代码行前暂停。如果想观察包含断点的行运行时发生的情况,你必须至少运行一个语句。为此,使用“逐语句”或者“逐过程”。

在你试图隔离一个问题时,请记住语句可能会给一个变量分配了一个不正确的值,从而间接引起错误。使用“本地”窗口、快速监视、监视表达式或者“立即”窗口,可在中断模式中检查变量和属性的值。

Stop语句的使用

在一个过程中放置一个Stop语句是设置断点的备用方法。一旦VB 遇到一个Stop语句,它会暂停执行并切换到中断模式。虽然Stop语句的作用与断点类似,但它不能以与断点同样的方式进行设置和清除。

请记住,Stop语句除了暂时停止执行外没有其他作用,而End语句可停止执行、重新设置变量并返回到设计时。你通常可单击“运行”菜单上的“继续”以继续应用程序的运行。

[此贴子已经被作者于2005-1-22 0:49:31编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:45 | 显示全部楼层

运行应用程序的选定部分

如果你能识别引起错误的语句,设置一个断点可帮助你进行问题的定位。然而,更普遍的情况是,你仅知道引起错误的大致代码范围。断点可帮助你将问题代码区隔离出来。然后,你可使用“逐语句”和“逐过程”来观察每条语句的效果。如果有必要,你还可跳过这些语句或返回,以便从新的一行开始执行。

步骤

模式描述

逐语句

运行当前语句并在下一语句中断,甚至下一语句在另一过程中。

逐过程

运行被当前行调用的整个过程并在当前行的下一行中断。

跳出

运行当前过程的余下语句并在调用该过程的语句的下一语句中断。

注:你必须在中断模式中使用这些命令。在设计时或运行时无法获得这些命令。

“逐语句”的使用

使用“逐语句”可一次运行一个语句(这也称作单步执行)。在整个代码中使用“逐语句”单步执行语句时,Visual Basic 暂时切换到运行时,运行当前语句并前移到下一语句。然后,Visual Basic 切换回中断模式。要想按照这种方法在代码中单步执行,单击“调试”工具栏上的“逐语句”按钮。

注:Visual Basic 允许你逐步进入每个独立的语句,甚至这些独立的语句位于同一行。一个代码行可包含两个或多个用冒号隔开的语句。Visual Basic 使用方框来表明下一个要执行的语句。断点仅适用于多语句行的第一个语句。

“逐过程”的使用

“逐过程”与“逐语句”相似,只有在当前的语句含有一个对过程的调用时,两者才会有差异。“逐语句”将进入被调用的过程,并在该过程中执行一个语句,与它不同的是,“逐过程”把被调用的过程视为一个基本单位来执行,然后转回到当前过程的下一语句。要使代码按这种方式执行,单击“调试”工具栏上的“逐过程”。

例如,假设一个语句调用过程SetAlarmTime。如果你选择“逐语句”,模块显示SetAlarmTime过程并把过程的开始语句设置为当前语句。这最好只是在你想分析SetAlarmTime内部代码时选用。如果使用“逐过程”,模块继续显示当前过程,除非SetAlarmTime包含一个断点或一个Stop语句,否则将执行到调用SetAlarmTime语句的下一语句。如果你想保持在同一级代码中以及不需要分析SetAlarmTime过程,可使用“逐过程”。

你可随意交替使用“逐语句”和“逐过程”。在任何给定时间所使用的命令取决于你想分析哪部分代码。

“跳出”的使用

“跳出”与“逐语句”和“逐过程”类似,只是它执行当前过程中剩余未执行的代码。如果该过程被另一过程调用,“跳出”命令会使代码执行到调用语句的下一语句。要使代码按此方式执行,单击“调试”工具栏上的“跳出”按钮。

跳过部分代码

当应用程序处于中断模式时,你可以在代码中选定想执行到哪一行语句才停止,然后单击“调试”菜单上的“运行到光标处”,这样,应用程序将会从当前语句执行到你所选定的语句,从而“跳过”你不感兴趣的代码部分,如大型循环等。

设置下一条语句

在调试或试验一个应用程序时,你可以在当前过程中任意行选定一个语句,然后单击“调试”菜单上的“设置下一条语句”来跳过某些代码段—例如,包含已知错误的代码段,这样,你可继续跟踪其他问题。或者你想返回到当前语句前面的语句,以便使用不同的变量值或属性值对那部分代码进行测试。

显示下一条语句

你可单击“调试”菜单上的“显示下一条语句”来将光标移到下一行会被执行的程序。如果你已在执行一个错误处理程序的代码但不能确认将执行的下一语句,这个特征是很方便的。“显示下一条语句”仅可在中断模式中使用。

监视调用堆栈

“调用堆栈”对话框(“调试”菜单或工具栏)显示所有活动的过程调用列表;你只有在应用程序处于中断模式时,才可显示“调用堆栈”对话框。活动的过程调用是应用程序中已开始执行但尚未完成的过程。使用活动的过程调用列表,可帮助你对正在执行一系列嵌套过程的应用程序的运行进行跟踪。例如,一个事件过程调用第二个过程,第二个过程又调用第三个过程—在启动这条链的事件过程结束前的所有过程调用。这些嵌套的过程调用很难进行跟踪,它们使调试进程变得复杂。

跟踪嵌套的过程

“调用堆栈”对话框列出了一系列嵌套的调用中所有活动的过程调用。它把最早的活动过程调用置于列表的底部,顺序向上添加过程调用。给出的每个过程的信息以模块名开始,后跟被调用的过程名。你可单击“调用堆栈”对话框上的“显示”来显示一个过程中的语句,该语句将应用程序的控制流传递给列表中的下一过程。

注:由于“调用堆栈”对话框不显示分配给类的一个实例的变量,因此它不能区分类的多个实例。

使用“立即”窗口测试数据和过程

有时,当你调试或试验某个应用程序时,可能想逐个运行过程、评估表达式或为变量/属性分配新值。你可以使用“立即”窗口来完成这些任务。通过在“立即”窗口显示表达式的值来评估表达式。

在“立即”窗口中显示信息

可使用下列两种方法在“立即”窗口中显示信息:

在应用程序代码中加入Debug.Print语句;

直接在“立即”窗口输入使用Print方法的语句。

你不必中断执行来获得应用程序执行情况的反馈信息。你可以在运行应用程序时查看显示的数据或其他消息。

反馈被显示在另一单独区域(“立即”窗口),因此它不会干扰用户看到的输出。

使用应用程序代码显示

在你加入Debug对象限定句处,Print方法把输出结果发送给“立即”窗口。例如,下列语句在每次运行时将在“立即”窗口显示Salary的值。

Debug.Print "Salary = "; Salary

若应用程序中存在某个特殊的地方,已知变量(本例中为Salary)在该处会发生改变,此时使用Print方法的效果最好。例如,你可能把上例中的语句放在一个循环中反复显示改变的Salary值。

直接在“立即”窗口中显示

进入中断模式后,你可把焦点移到“立即”窗口上,以检测数据。你可在“立即”窗口中评估任何有效的表达式,包括属性的表达式。当前活动的模块决定表达式的范围。输入一个使用Print方法的数据,然后按Enter(回车)键来查看结果。“?”符是Print方法的简化符号。

为变量和属性分配值

在你开始隔离错误的可能原因时,你可能想测试某个数据值对程序的影响。在中断模式中,你可在“立即”窗口中使用类似下列的语句来赋值:

VScroll1.Value = 100

MaxRows = 50

第一个语句改变VScroll对象的一个属性值,第二个语句给变量MaxRows赋值。

在你为一个或多个属性和变量赋值后,你可继续执行程序以观察结果或者你可测试这个改变对程序的影响。

使用“立即”窗口测试过程

“立即”窗口评估任何有效的Visual Basic 可执行语句,但它不接受数据声明。但是你可输入对过程的调用,从而使你可测试给定的变量值对某个过程的可能影响。你可把在模块中输入的语句输入“立即”窗口(中断模式中),如下面语句所示:

X = Quadratic(2, 8, 8)

DisplayGraph 50, Arr1

Form_MouseDown 1, 0, 100, 100

当你按下Enter键时,Visual Basic 切换到运行时以运行该语句,然后返回到中断模式。此时,你可查看结果并测试对变量或属性值的任何可能影响。

如果Option Explicit生效(要求所有变量声明是显式的),你输入“立即”窗口的任何变量必须已在当前范围内声明。适用于过程调用的范围同样适用于变量。你可调用当前活动的窗体中的任何过程。你通常可调用模块中的任何过程,除非你将一个过程定义为Private,此时你只能调用模块中正在执行的过程。

你可使用“立即”窗口来反复运行一个过程,测试不同条件的效果。每个独立的过程调用被Visual Basic 当作一个独立实例。这使得你可单独测试过程中每个实例的变量和属性设置。“调用堆栈”对话框显示“立即”窗口的每个命令运行的过程的列表。列表的上部为新运行的过程。你可使用“调用堆栈”对话框来选择一个过程的任何实例,然后在“立即”窗口中显示这个过程的变量值。

注:虽然“立即”窗口支持大多数语句,但对于一个控制结构,只有当它能在一行中完整表达时才有效;使用冒号来隔开组成控制结构中的各个语句。

检查错误号

你可使用“立即”窗口来显示与某个特定错误号相关的消息。例如,如果你在“立即”窗口内输入语句Error 58,然后按Enter键来运行该语句,那么将显示相应的错误消息(“文件已存在”)。

使用“立即”窗口的提示

在“立即”窗口中,你可使用下列快捷方法:

在输入一个语句后,你可把光标移回该语句,在此行的任意处按下Enter键,再次运行它。

在按下Enter前,你可编辑当前语句来改变其效果。

你可使用鼠标或箭头键在“立即”窗口内移动。除非光标位于你想运行的语句处,否则不要按Enter键。

CTRL+HOME键可使光标移到“立即”窗口的顶行;CTRL+END键可使光标移到“立即”窗口的底行。

HOME和END键可使光标移到当前行的开始和结束处。

特殊的调试考虑

某些事件使用了Microsoft Windows的公用部分,这会使调试应用程序面临特殊的问题。意识到这些特殊问题的存在是很重要的,不要让这些问题扰乱或使特殊进程变得复杂。

如果你记住中断模式如何处理与应用程序的预计结果不同的事件,你通常可找到解决方案。在一些事件过程中,你可能需要使用Debug.Print语句来监视变量或属性的值,而不是使用监视表达式或断点。你可能还需要根据事件的顺序改变变量的值。这将在下面主题中论述。

在MouseDown或KeyDown事件中中断执行

如果你在一个MouseDown事件过程中中断执行,你可释放鼠标按钮或者使用鼠标完成任何任务。然而继续执行程序时,应用程序会假定鼠标按钮仍然是按下的,直到你再此按下鼠标按钮并释放,才可获得一个MouseUp事件。

当在运行时按下鼠标按钮,假定在MouseDown事件过程中有一个断点,你再次在该事件过程中中断执行。在这种情况下,你永远不会获得MouseUp事件。通常的解决方案是清除MouseDown过程中的断点。

如果你在KeyDown过程中中断执行,考虑类似问题。如果你在KeyDown过程中保留了一个断点,你可能不再获得KeyUp事件。

在GotFocus或LostFocus事件过程中中断执行

如果你在GotFocus或LostFocus事件过程中中断执行,系统消息的定时会引起结果的不一致。使用Debug.Print语句而不是在GotFocus或LostFocus事件过程中使用断点。

调试的提示

简化调试的一些方法如下:

当应用程序不能产生正确结果时,浏览整个代码并试图找到可能引起问题的语句。在这些语句处设置断点并重新启动应用程序。

当程序停止时,测试重要变量和属性的值。使用“快速监视”或者设置监视表达式来监视这些值。使用“立即”窗口来检测变量和表达式。

选中“选项”对话框(“工具”菜单)的“通用”选项卡上的“发生错误则中断”,以确定错误发生的位置。逐步执行代码,使用监视表达式和“本地”窗口来监视代码运行时的变量变化情况。

如果错误发生在一个循环中,定义一个中断表达式来确定问题发生在何处。结合使用“立即”窗口和“设置下一条语句”,以便在改正错误后重新运行循环。

[此贴子已经被作者于2005-1-22 0:50:04编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-1-22 00:58 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-12-12 05:01 , Processed in 0.060871 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表