Error Trapping and Capturing Third-Party Output in WSH

In my last article we took an in-depth look at how to create log files for your scripts. In this article we are going to expand on that concept by teaching you how to trap events for a log file and how to capture third-party program output.

Chances are that if you’re logging the actions of one of your scripts, what you really want to know is what, if anything went wrong.  After all, you’ve spent all that hard work making your life easier; it would be good to know it’s working as planned.

The easiest way to do this is to know what actions produced errors.  The method of detecting errors with your script is known as error trapping.  Error handling is how your script reacts to that error.

Understand that both error trapping and error handling are far beyond the scope of this article.  I just want to introduce the ideas to you because it’s often useful to trap errors when executing third-party programs as you’ll see later in this article.

For our purposes we’re going to keep things simple.  WSH provides us with the Err object.  Any time an error occurs during execution, the error level is raised.  We can make frequent checks to the Err object to know when something hasn’t gone as planned.

If we’re going to be handling our own errors, we don’t want our script to crash if one is encountered.  Therefore, we need to tell the script debugger to let our script run no matter what.  We do that by starting our script with the following line.

On Error Resume Next

This statement tells the script debugger to continue to the next operation whenever it encounters an operation that it can’t complete.  While this line is typically found at the beginning of a script file, you can use it within individual functions and subroutines.

Properties

object.Number [= number]

object.Source [= string]

object.Description [= string]

object.HelpFile [= path]

object.HelpContext [= context]

 

Methods

object.Raise number[, source, description, helpfile, helpcontext]

object.Clear

The Err object has several methods and properties.  The Number property is used to retrieve or set a runtime error number.  The Source property returns the source of an error in the form of a class name or programmatic ID.  The Description property is used to set or retrieve a text-based “friendly” description.  The HelpFile and HelpContext properties are used to set and retrieve the location of local help file and a context ID within that help file, respectively.

{mospagebreak title=Trapping Errors}

The Err object is used for every error that occurs.  Each time you read its value, you must clear it before moving on with the Clear method.  You may also choose to create your own errors in some circumstances.  You can do that with the Raise method.

The Err object is initialized by the VBScript Engine every time a script is executed.  You do not need to instantiate it in your code to use it.

A basic If routine is all you need for an error check.  You’re simply going to query the Err number property to see if it’s anything other than 0.  Try this for example:

If Err.Number <> 0 Then

   Wscript.Echo "Error: " & Err.Number

   Wscript.Echo "Description: " & Err.Description

   Wscript.Echo "Source: " & Err.Source

   Err.Clear

End If

This is a simple error-check.  We can use this same code to write to a log file instead.  The key here to is remember to clear the error object to prevent it from combining multiple errors.

At times it may be useful to flag errors while using your own error-handling method.  Let’s say your code is designed to respond to a certain error.  In that case we can still use the Err object for logging purposes.

Set objFso = CreateObject("Scripting.FileSystemObject")

 

If objFso.FileExists("nosuch.file") Then

   Err.Raise 53,, "Cannot find specified file"

   Call errChk

   objFso.CreateTextFile("nosuch.file")

End If

 

Sub errChk

   If Err.number <> 0 Then

       Wscript.Echo "Error: " & Err.Number

       Wscript.Echo "Description: " & Err.Description

       Wscript.Echo "Source: " & Err.Source

       Err.Clear

   End If

End Sub

Here we’ve manually raised an Error 53 which is a File Not Found error.  Our script is set to handle this by creating the file that doesn’t exist but our error handler still gets a chance to log it if we so choose.

So why did we have to create the error?  The answer is simple; because the FileExists method returns an error code of 0.  The method was able to complete successfully.

{mospagebreak title=Logging to the Event Log}

Logging to a simple log file may not always be enough.  If you were performing system processes or automating applications you may wish to keep an error record in the actual Windows Event Log.

Value

Event Type

0

SUCCESS

1

ERROR

2

WARNING

4

INFORMATION

8

AUDIT_SUCCESS

16

AUDIT_FAILURE

Luckily, WSH provides an easy way for us to do that as well.  The WshShell object has a LogEvent method that allows us to record our events in the Application Event Log.

The LogEvent method can log to either the local computer or to a remote system.  This is nice if you are monitoring several different machines at the same time from one location such as a server.  Keep in mind also that event logging is always enabled on any Microsoft Windows system.

object.LogEvent value, string[, computername]

The LogEvent method accepts two required attributes.  The first is the numerical value for the Event Type.  You can find the list of possible values in the table above.  The second attribute is the text string that contains the description for the event.  A third optional attribute is used to specify the name of a remote computer to log the event to.

Set objShell = WScript.CreateObject("Wscript.Shell")

 

objShell.LogEvent 0,"Test Success Event"

objShell.LogEvent 1,"Test Error Event"

objShell.LogEvent 2,"Test Warning Event"

objShell.LogEvent 4, "Test Information Event"

objShell.LogEvent 8, "Test Success Audit Event"

objShell.LogEvent 16, "Test Failure Audit Event"

The above code will log one of each of the event types to the Applications Event Log.  You can verify this by clicking start, Run…, and typing eventvwr.msc in the Run dialog box to start the system’s Event Viewer.  Select Application in the left pane to view the Application log.

All events logged in this manner will have the Source “WSH” for Windows Script Host.  The Event ID will match the Event Type provided.

{mospagebreak title=Capturing Third-Party Output}

Now that we know how to log events in our script in several different ways, let’s take a look at some things worth logging.  One of the most useful purposes for logging in a script is to monitor the activity of a third-party program.

Perhaps you’re writing a script that runs every hour and tests the TCP/IP connectivity of every computer on your network.  A simple ping test is all that is required.  It seems logical that you would be able to run ping.exe from within your script, but how can you capture its output to determine whether or not it was successful?

This is where standard streams come into play.  In my last article, “Handling User Input in WSH,” we learned how to use two of the standard streams, standard input (StdIn) and standard output (StdOut).  We’re going to make use of both of those in this segment as well but I am also going to show you the third and final standard stream, standard error (StdErr).

Before we get too far ahead of ourselves, let’s take a quick look at how to run third-party programs in WSH.  The WshShell object provides us with the Run method and the Exec method.  Both are able to start applications and execute command lines directly.

The Run method can be compared to running an application from the Run dialog box.  The Exec method is the same as executing a command line at the command prompt.

When executing command lines there is a slight difference between the two:  the Run method will create a separate instance, or window, for the running process while the Exec method will run within the same instance.  For this reason we will use the Exec method so that output is directed to the same window in which our script is running.

object.Run strCommand[, intWindowStyle][, bWaitOnReturn]

object.Exec strCommand

The Exec method accepts a single string attribute that is the command line to be executed.  It returns a WshShellExec object.  This object has a property for each of the three standard data streams.  Each of these properties returns a read-only text string.

First, let’s take a look at how to actually execute a command line.  This particular script is going to ping the localhost address.  The script should not fail if you have a network adapter installed.

strCommand = "ping.exe 127.0.0.1"

 

Set WshShell = CreateObject("WScript.Shell")

Set WshShellExec = WshShell.Exec(strCommand)

Executing this code snippet will not output anything on the screen even though the ping command is executed.  That’s because its output is sent to the WshShellExec object’s StdOut stream.  You can retrieve the information by adding the following lines.

sOutput = WshShellExec.StdOut.ReadAll

Wscript.StdOut.Write sOutput

This code simply reads the WshShellExec’s StdOut stream into a string variable and then writes it to the Wscript object’s StdOut stream which writes it to the command window.

If we use this example, we can build a basic error-handling routine.

Const WshFinished = 1

Const WshFailed = 2

strCommand = "ping.exe 127.0.0.1"

 

Set WshShell = CreateObject("WScript.Shell")

Set WshShellExec = WshShell.Exec(strCommand)

 

Select Case WshShellExec.Status

   Case WshFinished

       sOutput = WshShellExec.StdOut.ReadAll

   Case WshFailed

       sOutput = WshShellExec.StdErr.ReadAll

End Select

 

WScript.StdOut.Write sOutput

In this code example we check the status of the WshShellExec object.  If it was successful, we grab the standard output stream to get the command’s output.  If it failed, we instead turn to the standard error object to see what went wrong.

We could easily use the information in sOutput to generate log entries.  You can see now how these two ideas work hand-in-hand.

I’ve given you some ways to write better, more professional code.  I hope that you’ll take the time to implement them into your own scripts.  Proper error-handling can be very useful if you ever have to provide support for your scripts.  Good luck writing better scripts.  Until next time, keep coding!

[gp-comments width="770" linklove="off" ]