http://www.kellie.de/jw1/steve4.jpg |
When I first started writing DOS batch files the only error handling was through the IF ERRORLEVEL command. ERRORLEVEL is populated with the return code from the previous command. The return code is zero if the command completed successfully and something else if it failed. IF ERRORLEVEL evaluates to TRUE if the ERRORLEVEL is greater than or equal to the value supplied. So error handling in batch files consisted of a bunch of IF statements with the known ERRORLEVELs and associated GOTO statements.
IF ERRORLEVEL 10 GOTO Error10
IF ERRORLEVEL 5 GOTO Error5
IF ERRORLEVEL 1 GOTO Error1
GOTO Success
:Error10
ECHO You got your peanut butter in my chocolate
GOTO Success
:Error5
ECHO You got your chocolate in my peanut butter
GOTO Success
:Error1
ECHO I don't have chocolate or peanut butter
:Success
ECHO And now I'm hungry
IF ERRORLEVEL 5 GOTO Error5
IF ERRORLEVEL 1 GOTO Error1
GOTO Success
:Error10
ECHO You got your peanut butter in my chocolate
GOTO Success
:Error5
ECHO You got your chocolate in my peanut butter
GOTO Success
:Error1
ECHO I don't have chocolate or peanut butter
:Success
ECHO And now I'm hungry
Batch file processing has improved over the years. You can still use the IF ERRORLEVEL statement but if you have command extensions enabled then ERRORLEVEL is also an environment variable. The first three lines in the previous script could be reduced to the single line:
IF ERRORLEVEL 1 GOTO Error%ERRORLEVEL%
Also the IF statement is now more robust and allows for standard numerical comparisons as well grouping multiple commands in parentheses.
IF %ERRORLEVEL% EQU 10 (
ECHO You got your chocolate in my peanut butter
) ELSE (
IF %ERRORLEVEL% EQU 5 (
ECHO You got your peanut butter in my chocolate
) ELSE (
IF %ERRORLEVEL% GEQ 1 (
ECHO I don't have chocolate or peanut butter
)))
ECHO And now I'm hungry
ECHO You got your chocolate in my peanut butter
) ELSE (
IF %ERRORLEVEL% EQU 5 (
ECHO You got your peanut butter in my chocolate
) ELSE (
IF %ERRORLEVEL% GEQ 1 (
ECHO I don't have chocolate or peanut butter
)))
ECHO And now I'm hungry
Those are stupid examples. You have capabilities to not only provide user feedback, but to retry failed operations, solicit input from users, or enable other resolutions. Still, the facilities are rudimentary.
In vbScript the facilities improve. vbScript error handling starts with the On Error statement which defines whether error handling is on or off. On Error Goto 0 turns off internal error handling and lets your script fail on error. On Error Resume Next turns on error handling and enables the internal Err object. This object contains the error number as well as descriptive information.
On Error Resume Next
' Do something useful here
If Err.Number <> 0 Then
WScript.Echo "Error: " & Err.Number
WScript.Echo "Source: " & Err.Source
WScript.Echo "Description: " & Err.Description
Err.Clear
End If
' Do something useful here
If Err.Number <> 0 Then
WScript.Echo "Error: " & Err.Number
WScript.Echo "Source: " & Err.Source
WScript.Echo "Description: " & Err.Description
Err.Clear
End If
Note that we clear the error after reporting it. If the Err object doesn't get cleared then our next test of Err.Number might report the previous error.
This generic error handling can be enclosed in a user defined function and called for each error detected.
On Error Resume Next
' Do something useful
If Err > 0 Then
DisplayErrorInfo
End If
' Do something else useful
If Err > 0 Then
DisplayErrorInfo
End If
Sub DisplayErrorInfo
WScript.Echo "Error: : " & Err
WScript.Echo "Source : " & Err.Source
WScript.Echo "Description : " & Err.Description
Err.Clear
End Sub
' Do something useful
If Err > 0 Then
DisplayErrorInfo
End If
' Do something else useful
If Err > 0 Then
DisplayErrorInfo
End If
Sub DisplayErrorInfo
WScript.Echo "Error: : " & Err
WScript.Echo "Source : " & Err.Source
WScript.Echo "Description : " & Err.Description
Err.Clear
End Sub
If you know which error codes are returned by the application being called then you can do something more elegant than just return generic error information. Here is some code pinched from The Scripting Guys.
On Error Resume Next
Set objWMIService = GetObject("winmgmts:" _
For Each strTargetProc In arrTargetProcs
If colProcesses.Count = 0 Then
strComputer = "."
arrTargetProcs = Array("calc.exe","freecell.exe")
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
For Each strTargetProc In arrTargetProcs
Set colProcesses = objWMIService.ExecQuery _
("SELECT * FROM Win32_Process WHERE Name='" & strTargetProc & "'")
If colProcesses.Count = 0 Then
WScript.Echo VbCrLf & "No processes named " & strTargetProc & " found."
Else
For Each objProcess in colProcesses
WScript.Echo VbCrLf & "Process Name: " & objProcess.Name
Wscript.Echo "Process ID: " & objProcess.Handle
Wscript.Echo "Attempting to terminate process ..."
intTermProc = TerminateProcess(objProcess)
Next
End If
Next
'***********************************************************************
Function TerminateProcess(objProcess)
On Error Resume Next
intReturn = objProcess.Terminate
Select Case intReturn
Case 0 Wscript.Echo "Return code " & intReturn & _
" - Terminated"
" - Terminated"
Case 2 Wscript.Echo "Return code " & intReturn & _
" - Access denied"
" - Access denied"
Case 3 Wscript.Echo "Return code " & intReturn & _
" - Insufficient privilege"
" - Insufficient privilege"
Case 8 Wscript.Echo "Return code " & intReturn & _
" - Unknown failure"
" - Unknown failure"
Case 9 Wscript.Echo "Return code " & intReturn & _
" - Path not found"
" - Path not found"
Case 21 Wscript.Echo "Return code " & intReturn & _
" - Invalid parameter"
" - Invalid parameter"
Case Else Wscript.Echo "Return code " & intReturn & _
" - Unable to terminate for undetermined reason"
End Select
TerminateProcess = intReturn
End Function
Something else to note is that the Err object has the method Raise which allows you to generate an error on demand. This is useful for debugging error handling code or if you just want to punk your users. It is also useful if you need to pass errors to other modules. Note that you have to add the constant vbObjectError to the error number you want to raise to prevent collision with existing errors.
In the next post I will discuss error handling in PowerShell. In case you couldn't guess, it is far superior to DOS and vbScript.