Sometimes, you want to trap scripting errors before anyone notices. Good error handling is the key.
Graceful Error Handling
Sometimes, you want to trap scripting errors before anyone notices. Good error handling is the key.
- By Chris Brooke
- 05/01/2000
I remember a certain math teacher I had in high school.
She was so understanding. Whenever I made a mistake on
my homework, she would lean all 275 pounds on my desk,
put those big, bloodshot eyes right in front of mine,
and say:
“You Got It WRONG!”
Ever since then, I’ve tried to ensure that if I
do make a mistake, I fix it before anyone else notices.
As NT administrators, we must remain vigilant about errors.
We perform backups, install hardware RAID 5 arrays in
our mission-critical servers, and even implement expensive
single-IP clustering solutions to make sure that if—heaven
forbid—anything does go wrong, no one will notice
(we hope).
It’s important to be equally conscientious regarding
potential errors in your scripts. Exactly how idiot-proof
we can make our scripts depends on the particular idiot
(ourselves) running them. (I’ve always maintained
that as soon as you make something idiot-proof, someone
comes along and builds a better idiot!) As scripts become
more complex, the potential for errors increases greatly.
It’s vital to trap these errors as they happen and
deal with them gracefully. The alternative is the see-no-evil,
hear-no-evil approach—close your eyes, wait for something
to happen, and then blame the user!
To Err is Human, to Forgive… Err.Clear
There are several ways to handle errors in your scripts.
For instance, in “Supercharge Your
Scripts”
(October 1999), I put an “If…Then” section
at the beginning of the script to ensure that the user
had entered the correct command-line arguments. That’s
error handling! That kind of error handling is preventative;
whereas, here I’m focusing on corrective error handling.
Whenever an unexpected condition arises during script
execution, all information pertaining to that error is
passed automatically to the Err object. The Err object
is an intrinsic object, meaning it’s always there
when you run a script—just like the WScript object
I discussed last issue. In other words, you don’t
have to create an instance of it (via CreateObject) in
order to use it. Its Properties include Err.Number and
Err.Description, and it also has two Methods, Err.Clear
and Err.Raise. Err.Clear clears all the error properties,
and Err.Raise generates a runtime error in your script.
You can use this to simulate errors, so that you can properly
code to handle them.
The Details
The first requirement for using the Err object is to
include “On Error Resume Next” at the beginning
of your script. This enables error handling. Without this
line, any errors in your script cause execution to stop
and an error message to be displayed. With this line included,
execution continues with the line following the one that
caused the error. You can then place code in your script
(if necessary) to help it recover.
‘FakeError.vbs
Dim strX
On Error Resume Next
strX="Hello World!"
strX=strX+1 ‘Try to add a number to a string value.
Bad juju!
If Err.Number<>0 Then
WScript.Echo
& "You can’t do that! You caused
Error "
& Cstr(Err.Number) & " "
& Err.Description
Err.Clear
End If
WScript.Echo strX
WScript.Quit
This forces an error by trying to add an integer value
to a string. Figure 1 shows the output from this script.
As you can see in Figure 1, strX remained “Hello
World!” when we printed it at the end. The important
thing to note is that the script executed to the very
end. Without the error handling, it would have shut down
in line five.
|
Figure 1. With error handling,
the script still executes to the end. |
The Real World
Apply what you’ve just learned and write a script
for an all-too-common administrative task.
Situation: You’ve
just fired an employee for cause, and you want to force
a password change for every user in the domain, just in
case this guy tries to break in using someone else’s
password. There are many ways to do this. Here, you accomplish
this task using Software Artisans’ SA-Admin product,
which we’ve used in the past for user management.
‘ResetPW.vbs
Option Explicit
Dim objUser, iCount, iNum
Set objUser=CreateObject("SoftArtisans.User")
objUser.GetDomainController "MyDomain", True
iCount=objUser.UserCount
iNum=0
Do
‘Cycle through users
objUser.User=objUser.UserItem(iNum)
‘Resetting the change PW option as we
go
objUser.MustChangePW=True iNum=iNum+1
Loop Until iNum=iCount
Set objUser=Nothing ‘Kill the
object
WScript.Quit
As you can see, this is a rather simple script that cycles
through every user in the specified domain and sets the
MustChangePW property to true. This forces everyone to
change his or her password at the next logon (and is much
faster than using User Mangler—oops, I mean “Manager”—to
do it).
Unfortunately, there are at least two big problems with
this script. First, the domain name is hard-wired in the
code. Not very flexible, is it? Second, if the PDC is
unreachable, what happens to the script execution? Rather
than look for more bugs, use what you’ve learned
about error handling to write a better script.
‘ResetPW.vbs
‘‘**** Section 1 ****
Option Explicit
On Error Resume Next
Dim objArgs, objUser, iCount, iNum, strDomain
Set objArgs=Wscript.Arguments
‘**** Section 2 ****
‘Make sure they only pass one argument
If objArgs.Count<>1 Then
ShowUsage()
End If
‘**** Section 3 ****
strDomain=objArgs.Item(0)
Set objUser=CreateObject("SoftArtisans.User")
objUser.GetDomainController strDomain, True
If Err.Number<>0 Then ‘Can’t
find PDC
WScript.Echo
"Invalid Domain Controller or
PDC not available>"
WScript.Echo "Please check domain name."
ShowUsage()
End If
‘**** Section 4 ****
iNum=0
iCount=objUser.UserCount
Do
‘Cycle through users
objUser.User=objUser.UserItem(iNum)
‘Resetting the change PW option as we
go
objUser.MustChangePW=True
iNum=iNum+1
Loop Until iNum=iCount
Set objUser=Nothing
Set objArgs=Nothing
Wscript.Quit
Sub ShowUsage()
WScript.Echo "Please Enter the Domain
name."
WScript.Echo
"Example: cscript ResetPW.vbs
MyDomain"
WScript.Quit
End Sub
Section 2 shows that you don’t always have to use
the Err object to check for bad data. You can trap errors
without it. Here, a simple If…Then statement ensures
that only one argument (the domain name, one hopes) was
passed to the script. Just in case the user made an error
typing in the domain name, Section 3 makes sure it’s
valid and reachable. As long as Err.Number=0, the SoftArtisans.User
component was able to find the domain controller. If that’s
the case, the script is free to cycle through all the
accounts, resetting the MustChangePW Property to True.
In the ResetPW.vbs script, you’re merely trapping
the errors and gracefully exiting the script—an end
result similar to what would have happened if you had
left well enough alone. However, now that you’ve
trapped the errors, do something else with them. Your
homework is to modify ResetPW.vbs to allow user input
to correct both incorrectly entered arguments and invalid/unreachable
domains. I’ll have my version for you next month
in Error Handling, Part Deux. And don’t worry—I
won’t embarrass you in front of the class.
About the Author
Chris Brooke, MCSE, is a contributing editor for Redmond magazine and director of enterprise technology for ComponentSource. He specializes in development, integration services and network/Internet administration. Send questions or your favorite scripts to [email protected].