VBScript语言提供了两个语句和一个对象来处理"运行时错误",如下所示:
On Error Resume Next
语句On Error Goto 0
语句Err
对象On Error Resume Next
语句和On Error Goto 0
语句指明了当出现"运行时错误"时的处理方式。
当加上On Error Resume Next
语句后,如果后面的程序出现"运行时错误",会继续运行,不中断。
当加上On Error Goto 0
语句后,如果后面的程序出现"运行时错误",会显示"出错信息"并停止程序的执行。
Err
对象保存了“错误信息”
如果没有加上On Error Resume Next语句,当出现"运行时错误"时,会显示"出错信息"并停止程序的执行.
举例(/test.asp文件):
i = 1/0 '0作除数,产生"运行时错误",显示"出错信息"并停止程序的执行
Response.Write "除法执行后" '这句话将不会执行
结果:
Microsoft VBScript 运行时错误 错误 '800a000b'
被零除
/test.asp,行 2
当我们在某处加上On Error Resume Next这条语句后,随后的程序即便出现"运行时错误"时,也不会显示"出错信息",并且会继续运行下去.
举例:
On Error Resume Next '后面的程序即便出现"运行时错误"时,也会继续运行
i = 1/0 '0作除数,这是一种"运行时错误",但因为有了上面On Error Resume Next这句话,所以不会中断执行,而是会继续运行下去
Response.Write "除法执行后" '这句话将会执行
结果:
除法执行后
使用了On Error Resume Next之后,如果出错,那么Err对象中将放置最近一次出错的信息。
Err对象重要的属性有三个:Number, Source, Description,分别是错误号,错误来源,错误描述。
If Err then这样的写法等价于If Err.Number then
举例:
On Error Resume Next
i = 1/0 '第一个错误
undefined_function "test" '第二个错误,函数undefined_function未定义
Response.Write Err.Description
运行结果:
类型不匹配
可以看到,显示的是有关第二个错误的信息。
On Error Resume Next语句只作用于本级别的后续语句。不会作用于被调用的函数或子程序,也不会作用于父级别的程序段
On Error Resume Next语句如果出现在某个函数中,则只对本函数产生影响。对“主调函数”和“被调函数”均没有影响
一个子程序中如果没有On Error Resume Next语句,那么当错误在子程序中出现时,将会中断这个子程序的运行,跳转到调用这个子程序的外层程序.
如果外层程序在“调用这个子程序之前”包含有On Error Resume Next语句,则会接着执行“子程序调用”之后的语句。如果外层程序在“调用这个子程序之前”没有On Error Resume Next语句,那么就跳向更外一层。
这个过程一直重复,直到找到包含有On Error Resume Next语句的环境继续运行,如果最外层的程序也没有包含On Error Resume Next语句,那么将会使用缺省的错误处理方式,也就是显示错误信息并停止运行。
例子1:
On Error Resume Next
level_1 '本语句前有On Error Resume Next,因此level_1退出时,会继续向下执行
Response.Write "<p>level_1调用后</p>"
Sub level_1()
level_2 '本语句前没有On Error Resume Next,因此level_2退出到本子程序时,不会继续向下执行,而是接着向外层程序跳转
Response.Write "<p>level_2调用后</p>"
End Sub
Sub level_2()
i = 1/0 '本语句前没有On Error Resume Next,因此执行到此时,会中断本子程序的运行,跳转到外层程序
Response.Write "OK"
End Sub
结果:
level_1调用后
两个子程序中的Response.Write语句都没有执行。
解释:
level_2是最内层的程序,因为i = 1/0语句前没有On Error Resume Next,所以执行到此时,会中断level_2这个子程序的运行,跳转到它的外层程序,也就是level_1子程序
但level_1子程序在调用level_2子程序之前,同样没有加On Error Resume Next,因此接着跳往外层程序,也就是"全局性程序".
"全局性程序"在调用level_1子程序之前,加了On Error Resume Next,因此会接着执行"level_1调用语句后面"的语句.
例子2:
改变一下上面的程序,在level_1子程序中的"level_2子程序调用语句"前加上On Error Resume Next
level_1
Response.Write "<p>level_1调用后</p>"
Sub level_1()
On Error Resume Next
level_2 '本语句前有On Error Resume Next,因此level_2退出到本子程序时,会继续执行下面的语句
Response.Write "<p>level_2调用后</p>"
End Sub
Sub level_2()
i = 1/0 '本语句前没有On Error Resume Next,因此执行到此时,会中断本子程序的运行,跳转到外层程序
Response.Write "OK"
End Sub
结果:
level_2调用后
level_1调用后
解释:
例子3:
改变一下上面的程序,在level_2子程序中的"i = 1/0"语句前加上On Error Resume Next
level_1
Response.Write "<p>level_1调用后</p>"
Sub level_1()
level_2
Response.Write "<p>level_2调用后</p>"
End Sub
Sub level_2()
On Error Resume Next
i = 1/0 '本语句前有On Error Resume Next,因此即便本语句有错误,也不会中断执行,而是会继续执行下面的语句
Response.Write " <p>i = 1/0 执行后</p>"
End Sub
结果:
i = 1/0 执行后
level_2调用后
level_1调用后
通过例子3可以看到,如果在子程序的开头放置一条On Error Resume Next语句,那么子程序中出现的运行期错误不会中止这个子程序的运行,即使自身出现错误也不会影响到外层程序。
这种方法通俗的讲就是:自己内部的错误,自己解决,不让它扩散出去。
因此编程时,推荐采用这种方法.
下面举个例子。有一个名为WriteNewFile
的函数,功能是:向一个文件中写入字符串.函数内部会对可能出现的错误进行处理,这样以来,错误就不会中断调用程序的运行:
'函数功能:创建一个名为strFileName的文件, 并且将字符串strContent写入文件
'如果函数执行成功,则返回值是true, 只要有错误发生,则返回值就是false
Function WriteNewFile(strFileName, strContent)
On Error Resume Next '关闭默认的错误处理器
WriteNewFile = Flase '函数默认的返回值
Set objFSO = CreateObject("scripting.FileSystemObject")
If Err.Number = 0 Then Set objFile = objFSO.CreateTextFile(strFileName,True)
If Err.Number = 0 Then objFile.WriteLine strContent
If Err.Number = 0 Then objFile.Close
If Err.Number = 0 Then WriteNewFile = True
End Function
上面的函数在处理每条语句前,先检查Err对象的Number属性。如果值为0(表明还没有出现错误),那么就能够继续对文件的创建和写入过程。如果错误发生了,脚本引擎将设置Err对象的属性的值,并且继续执行下一行语句。
只要没有错误出现,函数的返回值将设置为“True”。否则函数将返回“False”。
加上On Error Resume Next语句后,后面的程序即便出现"运行时错误"时,也会继续运行.可是如果希望后面的程序出现"运行时错误"时停止执行并显示错误,该怎么做呢?
答案是:使用On Error Goto 0 语句
在运行这个语句后,如果发生运行时错误,将采用缺省的处理方式,也就是,在调用链中逐步向外跳转,直到主页面代码,如果没有环境关闭缺省错误处理,那么网页的执行将停止并显示错误信息。
使用 On Error Goto 0 这条语句后,后面的程序一旦有错误发生就会提示错误,并结束脚本执行。
举例:
On Error Resume Next
Dim i
i = 1/0
Response.Write "第一个除法执行后"
On Error Goto 0 '后面的语句一旦有错误发生就会提示错误,并结束脚本执行
i = 1/0
Response.Write "第二个除法执行后"
运行结果:
第一个除法执行后Microsoft VBScript 运行时错误 错误 '800a000b'
被零除
/test.asp,行 7
可以看到,第一个Response.Write执行并输出了内容,第二个Response.Write没有执行。
Err对象存储了关于运行期错误的信息
下表给出了Err对象的属性。
Err对象的属性 | 功能说明 |
---|---|
Description | 对于错误的详细描述 |
Number | 错误编号,唯一性的标识一类错误.两种不同类型的错误,编号不同.(缺省属性) |
Source | 产生错误的对象的名称 |
使用Number属性值可以检查发生了哪种错误,使用Source和Description属性值得到错误的具体信息。
下表给出了Err对象的方法。
Err对象的方法 | 功能说明 |
---|---|
Clear | 清除当前所有的Err对象设置 |
Raise | 产生一个运行时错误 |
所谓“自定义错误”就是这个错误的Number, Source, Description三个属性可以由编程人员设定.
生成“自定义错误”的方法:首先把Err对象的Number, Source, Description三个属性设置成所希望的值,然后调用Raise方法来产生这种错误.
“自定义错误”和“普通错误”一样,会停止本级程序的运行,把错误沿调用链向回传递。
下面的例子中,函数Read_File会读取一个文本文件,如果出现自定义的错误,就向外层程序抛出,由外层程序决定如何处理错误。
注意常数vbObjectError的用法。把任意一个数字加到此常数上,就能够保证“自定义错误”的编号(number值)不会和任何“已定义错误”的编号重合。
Function Read_File(strFileName) '返回文件内容
On Error Resume Next
Read_File = "" '函数默认的返回值
Set objFSO = CreateObject("scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("strFileName", ForReading)
Select Case Err.Number
Case 0 '没错误, 不做任何动作
Case 50,53 '文件或者路径不存在
'下面将会创建一个“自定义错误”,并且向外层程序抛出这个错误
intErrNumber = vbObjectError + 1073 '“自定义错误”的编号
strErrDescription = "文件被删除了,或是移动到了其他位置" '对于“错误”具体情况的描述
strErrSource = "本错误是在Read_File函数中产生的" '表明“错误”的发生地点,也就是在Read_File这个函数中
Err.Raise intErrNumber, strErrSource, strErrDescription '生成“自定义错误”
Exit Function
Case Else '其他类型的错误
Err.Raise Err.Number, Err.Source, Err.Description '对于“已定义错误”,保持原貌
Exit Function
End Select
Read_File = objFile.ReadAll '文件打开成功,返回文件的内容
objFile.Close
End Function
函数Read_File运行时,可能会产生“自定义错误”,因此调用这个函数前加上On Error Resume Next语句,这样就能捕获到这个函数产生的错误,然后作处理。
On Error Resume Next
strContent = Read_File("myfile.txt")
If Err.Number = 0 Then
Response.Write "文件内容:<br/>" & strContent
Else
Response.Write "执行Read_File函数时产生错误。" & Err.Source & "<br/>" & Err.Description
End If