Programming and Scripting Techniques for Windows Administrators

Author: NetworkAdminKB.com

Created: 2008-05-17

Modified: 2008-12-03

 

In this article I will be discussing methods that network administrators can use to improve their scripting ability and develop script libraries that will eventually lead to their ability to develop larger and more complicated projects.

 

This article is written for Windows Administrators, because of this I will be using VBScript as the demonstration language for all scripts.  However, the methods demonstrated here can be applied to any programming or scripting language.

 

Throughout this guide I will show you how to develop simplified subroutines and functions that you can then put into a library or collection for re-use in your programs.  By doing this you will be able to develop projects faster.

 

Simplifying String Manipulation in VBScript

If you have programmed or written scripts for any length of time you will discover that string manipulation is a large part of any program or script.  You will retrieve strings from users, from objects, from arrays, etc.  You will use these strings to determine what to do, display them to the screen, as input to other objects or arrays, etc.

 

String manipulation is not limited to just the “string” data type.  It will also includes numerical data type and date types.  Being able to manipulate date types as strings to display them in a useful manner will be discussed later in this article.

 

To start simplifying strings in VBScript we should first start with simplifying the basic comparison operators in VBScript.  The most basic comparison operation in VBScript is the StrComp function.  StrComp by itself will return a value indicating the result of a string comparison.  This by itself is not necessarily useful because there can be four different results (refer to the VBScript Help file for more information).   However, we can create a function based upon StrComp that will return a boolean indicating if the strings being compared are equal or not.  This can be very useful because it will help simplify your programming code.

 

Before we start building a basic string comparison function we need to start with an even more basic function.  The StrComp, InStr, InStrReverse, Replace, Split and Filter functions all use VBScript comparison constants (vbBinaryCompare,  vbTextCompare) to allow the programmer to indicated if they wish to use case sensitive or case insensitive comparisons when using these functions.  We should simplify case sensitive or case insensitive comparison interface for these functions first.  Doing so is easy.

 

'********************************************************************

'*

'* Function ReturnCaseComparisonValue

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2008-08-05

'* Modified: 2008-08-05

'*

'* Purpose: Returns the proper VB String Comparison Constant based on

'*            the provided boolean in blnCase

'*

'* Input:   blnCase   A boolean indicating case sensitive comparisons.

'*                      True  = Case Sensitive comparison

'*                      False = Case Insensitive comparison (Default)

'*

'* Output:  Returns the proper VB String Comparison Constant based on

'*            the provided boolean in blnCase.

'*            vbBinaryCompare 0 Perform a binary (case sensitive) comparison.

'*            vbTextCompare   1 Perform a textual (case insensitive) comparison.

'*          Returns vbTextCompare if blnCase is anything other than True

'*             (ie Empty, Null, a String, etc).

'*

'********************************************************************

Function ReturnCaseComparisonValue(ByVal blnCase)

  'Version 1.0 2008-05-18

 

  Select Case blnCase

    Case True

      'vbBinaryCompare provides case sensitive comparison

      ReturnCaseComparisonValue = vbBinaryCompare

    Case False

      'vbTextCompare provides case insensitive comparison

      ReturnCaseComparisonValue = vbTextCompare

    Case Else

      'Return Defuault Setting.

      ReturnCaseComparisonValue = vbTextCompare

  End Select 'blnCase

End Function 'ReturnCaseComparisonValue

 

Programming Note: I have included a “Default” return value in this function to accommodate the best case comparison of “case insensitive” when an unknown variable type is passed.  Returning a default value is a very basic best practice that should be implemented into most functions that are created.  Ultimately default values help to prevent errors or more gracefully detect errors without introducing a runtime error or complicated error handling that will be discussed later.

 

With this building block function completed we can now return back to the need to create a function based upon StrComp that will return a boolean indicating if the strings being compared are equal or not.  Using the ReturnCaseComparisonValue function will help keep this function and other future functions as simple as possible.  It will also help to increase the speed to in which we can write the functions and procedures that will make up our scripts.

 

'********************************************************************

'*

'* Function blnStrComp

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2008-05-18

'* Modified: 2008-05-18

'*

'* Purpose: Returns a boolean indicating if the strings provided are

'*            equal based on case sensitive or case insensitive

'*            comparison.  Numbers are converted to strings for

'*            comparison.

'*

'* Input:   strComp1  A string to compare with strComp2

'*          strComp2  A string to compare with strComp1

'*          blnCase   A boolean indicating case sensitive comparisons.

'*                      True  = Case Sensitive comparison

'*                      False = Case Insensitive comparison (Default)

'*

'*  Notes: UCase and LCase convert numbers to strings for comparison

'*

'* Output:  Returns a boolean indicating if the strings provided are

'*            equal based on case sensitive or case insensitive

'*            comparison.

'*            True  = The strings are equal

'*            False = The strings are not equal.

'*

'* Calls:

'* ReturnCaseComparisonValue

'********************************************************************

Function blnStrComp(ByVal strComp1, ByVal strComp2, ByVal blnCase)

  'Version 1.0 2008-05-18

 

  Select Case StrComp(strComp1, strComp2, ReturnCaseComparisonValue(blnCase))

    Case 0

      blnStrComp = True

    Case Null, -1, 1

      blnStrComp = False

    Case Else

      'Return Default Value

      blnStrComp = False

  End Select 'StrComp(strComp1, strComp2, ReturnCaseComparisonValue(blnCase))

End Function 'blnStrComp

 

By simplifying the 4 possible return values into two results (True or False) we can perform basic comparisons very quickly in our code now using a simple If statement like so:

 

If blnStrComp("abc", "ABC", False) Then Wscript.Echo "they match"

 

We should now look at applying this same thought process of only returning a Boolean in comparisons to another VBScript native string comparison function; InStr.  By default the InStr function allow us to compare strings, but it also has a more complicated interface and numerous results that need to be interpreted within your program.  By creating functions that simplify the return value to a boolean value (True, False) we can greatly simplify our code when doing basic comparisons.  The more complicated version of the InStr native function is always available if it is needed within your program.

 

The next function we should create is blnInStr.  This function’s goal is to return a Boolean if one string exists inside another based on the desired case comparison.  This is usually very useful when working with strings.

 

'********************************************************************

'*

'* Function blnInStr

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2004-06-29

'* Modified: 2008-08-18

'*

'* Purpose: Returns a Boolean indicating if the the specific string

'*            is contained in another string based on case sensitive

'*            or case insensitive comparison.

'*

'* Input:   strSearch A string to be searched.

'*          strMatch  A string to search for in strSearch.

'*          blnCase   A boolean indicating case sensitive comparisons.

'*                      True  = Case Sensitive comparison

'*                      False = Case Insensitive comparison (Default)

'*

'* Output:  True or False

'*          True  = strMatch was found in strSearch.

'*          False = strMatch was Not found in strSearch.

'*

'* Calls:

'* ReturnCaseComparisonValue

'********************************************************************

Function blnInStr(ByVal strSearch, ByVal strMatch, ByVal blnCase)

   'Version 1.1 2008-05-18

   Dim blnTemp

   

   blnTemp = False

  

   If InStr(1, strSearch, strMatch, ReturnCaseComparisonValue(blnCase)) > 0 Then

     blnTemp = True

   End If 'InStr(1, strSearch, strMatch, ReturnCaseComparisonValue(blnCase)) > 0

 

   blnInStr = blnTemp

End Function 'blnInStr

 

Since the InStr and InStrRev functions provides many different results depending on the type of comparison being made I have found the following are common comparison operations frequently used in scripting and programming.  You should work on developing these functions and adding them to your library as they will be of great value when working with strings.

 

IsFirstChar: returns a Boolean indicating if the First Character(s) of a string matches the string provided.

IsLastChar: returns a Boolean indicating if the Last Character(s) of a string matches the string provided.

 

We have now covered the four basic string comparison operations which are:

1)      Check if one string exists inside another.

2)      Check if two complete strings match each other.

3)      Check if the start of one string matches another string.

4)      Check if the end of one string matches another string.

 

Expanding on the basic comparison operators I have found that working with strings usually involves the following basic actions; comparison, manipulation, conversion, substring separation, and concatenation.  String manipulation includes removing, replacing, reversing, creating, and sizing strings.  Conversion is the process of changing a string to and from any other data types.

 

Below is a table that shows the VBScript native functions that provide the type of operations specified along with the names of custom functions that I have written over the years to simplify common string actions.

 

String Comparison

String Manipulation

String Conversion

SubString Separation

String Concatenation

Native

Custom

Native

Custom

Native

Custom

Native

Custom

Native

Custom

StrComp

blnStrComp

Replace

LTrimText

Split

CSVSplit

Left

strBetween

&

LAddText

InStr

blnInStr

LTrim

RTrimText

Join

CSVJoin

Right

 

+

RAddText

InStrRev

IsFirstChar

RTrim

LChopText

CStr

 

MId

 

 

strInsert

=

IsLastChar

Trim

RChopText

 

 

 

 

 

 

 

IsString

StrReverse

CaseFormat

 

 

 

 

 

 

 

 

Space

LPadString

 

 

 

 

 

 

 

 

String

RPadString

 

 

 

 

 

 

 

 

LCase

CSVParse

 

 

 

 

 

 

 

 

UCase

ReplaceString

 

 

 

 

 

 

 

 

Len

 

 

 

 

 

 

 

 

From the name of the functions above you may be able to infer what each function does.  You should then try to define and write several functions as an exercise.  A good way to start would be to write out the purpose, input, and output for each function prior to writing it.

 

Some basic definitions that I developed in naming my functions

Trim: is the practice of searching for and removing the exact text that matches.

Chop: is the practice of removing a set number of characters up to and/or including that character.

Add: is the practice of concatenating one string to the other only if it does not already exist.

Pad: is the practice of adding the same character to the string until the string is a specific length.

Between: are the characters that are flanked by the same character (example: ab/cde/fg = cde).

CSV: Comma Separated Values that can include commas within quotes (“,”) as well.

 

The long term key to developing a library of functions that deal with strings will be to write them as needed during the development of a project.  As you start to do that you should think in the most basic terms possible when attacking the problem.  This means that you should look at the problem in terms of the basic categories listed above (comparison, manipulation, conversion, separation, or concatenation) and attack the problem in pieces.  Rather than write a function that does everything at once, you may need to develop some low level functions that break the string apart, compare the pieces, and then puts them back together again.

 

Simplifying and Converting Dates in VBScript

The VBScript provided functions to handle date and time are limited by their lack of formatting for YYYY-MM-DD formats and 24 hour time.  These two formats are best used in programs and scripts because they can be easily sorted.  As it pertains to programming, handling a date or time has only the following purposes; displaying as output, comparison to another date or time, adding or subtracting dates, or sorting multiple dates and times.  Most of these functions are provided natively in VBScript.  However, a sort-able date format is not provided.

 

The ISO 8601 is the format best suited for sorting dates in programs or scripts because it lends itself to be easily sorted with a simple text sort routine.  The ISO 8601 format that is recommended is YYYY-MM-DD HH:MM:SS where HH is in 24 hour time.

 

Note: While VBScript has a specific Date type (vbDate=7 as defined in the VarType function), the value is really just a string.  Because of this when dealing with a Date or Time in VBScript we will be doing noting more that working with a string.

 

The first step in converting any date or time to the ISO 8601 format is writing a function that will convert all single digit numbers to the two digit format (01…09).  The function is really simple.

 

'********************************************************************

'*

'* Function TwoDigit

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2006-01-11

'* Modified: 2006-01-11

'*

'* Purpose: Returns integers < 10 as two digits (01, 02,..,09)

'*

'*   Input:

'*        intDigit  Any integer value.

'*

'*  Output: Returns integers < 10 as a two digit string (01, 02,..,09).

'*          Returns integers >= 10 as the original integer.

'*

'********************************************************************

Function TwoDigit(ByVal intDigit)

  'Version: 1.0 2006-01-11

  intDigit = CInt(intDigit)

 

  If intDigit < 10 Then

    TwoDigit = "0" & CStr(intDigit)

  Else

    TwoDigit = intDigit

  End If 'intDigit < 10

End Function 'TwoDigit

 

The next step in converting to the ISO 8601 format will be to convert 12 hour time to 24 hour time.  This is a pretty straight forward process, break the Time apart, perform any math and put the pieces back together in the desired format.

 

'********************************************************************

'*

'* Function Time24Hour

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2006-01-11

'* Modified: 2006-01-11

'*

'* Purpose: Converts a 12 Hour Time to the 24 Hour time.  The 12 hour time

'*            can be passed in as a string or a date type.  The 24 hour

'*            time is returned as a sting type.

'*

'*   Input: strTime  A string or Date value in the format

'*                     7:45:41 PM  or 7:45:41 AM

'*

'*  Output: Returns the 24 hour time as a string in the format.

'*             19:45:41  or  07:45:41

'*

'* Calls:

'*   TwoDigit

'********************************************************************

Function Time24Hour(ByVal strTime)

  'Version: 1.0 2006-01-11

  Dim aryParts, aryTime, strNewTime

 

  aryParts = Split(strTime, " ")

  Select Case aryParts(1)

    Case "AM"

      aryTime = Split(aryParts(0), ":")

      If CInt(aryTime(0)) = 12 Then

            strNewTime = "00:" & aryTime(1) & ":" & aryTime(2)

      Else

        strNewTime = TwoDigit(aryTime(0)) & ":" & aryTime(1) & ":" & aryTime(2)

      End If 'CInt(aryTime(0)) = 12

    Case "PM"

      aryTime = Split(aryParts(0), ":")

      If CInt(aryTime(0)) = 12 Then

            strNewTime = aryTime(0) & ":" & aryTime(1) & ":" & aryTime(2)

      Else

        strNewTime = CInt(aryTime(0)) + 12 & ":" & aryTime(1) & ":" & aryTime(2)

      End If 'CInt(aryTime(0)) = 12

  End Select 'aryParts(1)

 

  Time24Hour = strNewTime

End Function 'Time24Hour

 

The last function to write is will convert the date and time format into the correct ISO 8601 format that we desire.  This function is written to be flexible so that any correctly formatted date, time, or date and time combination can be properly formatted in the ISO 8601 format and returned.

 

'********************************************************************

'*

'* Function DateTimeFormat

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2006-01-11

'* Modified: 2006-01-11

'*

'* Purpose: Returns the given Date/Time as a string in following format

'*            YYYY-MM-DD HH:MM:SS    (24h ISO 8601 Format).

'*

'*   Input:

'*    dtmDateValue  A string or Date/Time value in one of the following formats.

'*                    "1/10/2006 7:45:41 PM"

'*                    "1/10/2006"

'*                    "7:45:41 PM"

'*

'*   Notes: If only the date or time is passed in, then the corresponding

'*            result is also only the date or time.

'*

'*  Output: Returns the given Date as a string in the requested format.

'*

'* Calls:

'*   TwoDigit

'*   Time24Hour

'*

'********************************************************************

Function DateTimeFormat(ByVal dtmDateValue)

  'Version: 1.0 2006-01-11

  Dim aryParts, aryDate, strDate, strNewDate, strTime

  Dim strJoin

 

  strDate = CStr(dtmDateValue)

  strJoin = "-"

 

  'Year/Month/Date 24Time

  '4 digit year, 2 digit month, 2 digit day

  aryParts = Split(strDate, " ")

  aryDate = Split(aryParts(0),"/")

 

  If UBound(aryDate) = 2 Then

    Select Case UBound(aryParts)

      Case 0

      Case 1

      Case 2

        strTime = aryParts(1) & " " & aryParts(2)

        strTime = Time24Hour(strTime)

    End Select 'UBound(aryParts)

   

    If IsEmpty(strTime) Then

      strNewDate = aryDate(2) & strJoin & TwoDigit(aryDate(0)) & strJoin & _

                     TwoDigit(aryDate(1))

    Else

      strNewDate = aryDate(2) & strJoin & TwoDigit(aryDate(0)) & strJoin & _

                     TwoDigit(aryDate(1)) & " " & strTime

    End If 'strTime

  Else

    'Only the 12 hour time was passed, convert to 24 hour and return

    strNewDate = Time24Hour(dtmDateValue)

  End If 'UBound(aryDate) = 2    

 

  DateTimeFormat = strNewDate

End Function 'DateTimeFormat

 

With the DateTimeFormat function built you are now prepared to easily work with dates and times in your scripts.  The ease to which you can now sort dates and times will be beneficial in many programs to come.

 

Simplifying Arrays in VBScript

Being able to easily work with Arrays can be a cornerstone in easily implementing projects in VBScript.  The difficulty in working with Arrays is the tedious code you must write to add or remove objects to an array and the never ending battle about what size to predefine arrays. 

 

To make my program as “easy” to write as possible I have decided to always use dynamic arrays.  While the trade off is slightly slower performance, the ease to which you can reuse code and more quickly develop a project is well worth the trade off.  In general the trade off comes down the following facts

1)      The sizes of the arrays that a Windows Administrator usually deals with are 500,000 elements or less.

2)      The performance difference between dynamic array code and optimized code is usually measured in minutes not hours.

3)      The development time savings in using dynamic array code is significant on each project.  This reduces development time to less than half of using optimized code.

4)      The performance penalty comes from copying the array in memory during ReDim commands and when passing arrays ByVal and not ByRef.  This is still very fast on arrays under 500,000 elements. 

5)      Performance and optimization in other code may be more important than on the performance penalties mentioned above.  However, even highly optimized code is very rarely needed in the every day scripts that Windows Administrators may write.

 

The basic functionality that we need to develop for using dynamic arrays in VBScript are the following functions: ResizeOnAdd, ResizeOnDelete, IndexFind, and PrintArray.

 

The ResizeOnAdd function is provided below.  One nice feature that has been added to this function is that any variable can be passed as the array (aryAny).  The variable passed as aryAny should be defined in one of two ways, a standard variable (dim aryTemp) or as a dynamic array (dim aryTemp()).

 

'********************************************************************

'*

'* Function ResizeOnAdd

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2006-01-07

'* Modified: 2006-01-07

'*

'* Purpose: Return an array with the specified value added and

'*            with one more element in the size of the array.

'*

'*   Input: aryAny = The array to have the element added to

'*          anyVar = The variable to add to the array.

'*      blnVerbose = A Boolean indicating if error messages should

'*                     be displayed.

'*                      True  = Display Error Messages

'*                      False = Do NOT Display Error Messages

'*        

'*  Output: Returns the array with the specified value added at

'*            the end of the array.  This results in the size of

'*            the array increasing by 1.

'*          Return the original variable if the array passed is

'*            not actually an array.

'*

'*   Notes: This function adds the elements to the end of the array.

'*

'********************************************************************

Function ResizeOnAdd(ByVal aryAny, ByVal anyVar, ByVal blnVerbose)

  'Version: 1.0 2006-01-07

  Dim intUBound, intIndex

 

  If IsEmpty(aryAny) Then

    ReDim aryAny(0)

    aryAny(0) = anyVar

  Else  

    If IsArray(aryAny) Then

      intUBound = UBound(aryAny)

      ReDim Preserve aryAny(intUBound + 1)

      aryAny(intUBound + 1) = anyVar

    Else

      If blnVerbose Then

        Wscript.Echo "ResizeOnAdd", _

                     "Error: The aryAny parameter passed is not an array."

      End If 'blnVerbose

    End If 'IsArray(aryAny)

  End If 'IsEmpty(aryAny)

  ResizeOnAdd = aryAny

End Function 'ResizeOnAdd

 

ResizeOnDelete is a little more complicated to write because an item can be deleted from any location within the array.  The goal of ResizeOnDelete is to remove the desired value, and shrink the array appropriately.  This means that we must first find the element in the array by searching the array.  This leads us to the next function for use in on arrays, search an array.

 

When searching an array for a specific element and the function should return the indexed location of the value in the array.  The function should also return Empty if the value is not found in the array.  Below is an search function that I have created called SequentialSearch.  As the name indicates this function uses a simple sequential search to find the index of the desired element.

 

'********************************************************************

'*

'* Function SequentialSearch

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2005-01-10

'* Modified: 2005-01-10

'*

'* Purpose: Search for and Return the Index of an element in an 

'*             unorder/unsorted Array.

'*

'*   Input: aryAny = The unorder/unsorted Array to Search.

'*          anyVar = The item to search for in the Array

'*         blnCase = A boolean indicating a Case Sensitve Search.

'*                    True  = Case Sensitive

'*                    False = Case InSensitive

'*

'*  Output: Return the index of the location of the element, or Return

'*            Empty if the item is not found.

'*

'*   Notes: Assumes all items are unique.  If they are not unique the

'*            first item encountered is returned.

'*         

'********************************************************************

Function SequentialSearch(ByVal aryAny, ByVal anyVar, ByVal blnCase)

  'Version 1.0 2005-01-10

  Dim x

 

  SequentialSearch = Empty

 

  'Perform a sequential search on the array.

  For x = 0 to UBound(aryAny)

    If blnCase Then

      If anyVar = aryAny(x) Then

         SequentialSearch = x

         Exit For

      End If 'anyVar = aryAny(x)

    Else

      If LCase(anyVar) = LCase(aryAny(x)) Then

        SequentialSearch = x

        Exit For

      End If 'anyVar = aryAny(x)

    End If 'blnCase

  Next 'x

End Function 'SequentialSearch

 

Now that we can find elements and their location within the array the next step would be to write the ResizeOnDelete function.  This is left as an exercise, since all the building blocks are there.  The ResizeOnDelete function should perform the following: Return an array with the specified value removed and with one less element in the size of the array.

 

Simplifying Error Handling and Troubleshooting

Error handling and troubleshooting in VBScript can really be broken down into three categories.

  • Bad code (syntax errors)
  • Bad data
  • Misconfigured computer

 

Bad Code

Unlike true programming languages VBScript doesn’t provide a development interface that will do syntax checking and allow you step threw your program.  In VBScript you are forced to find syntax errors and bugs at runtime.  The number one biggest mistake that I see people make is putting “On Error Resume Next” somewhere at the top of the script.  This is the biggest mistake you can make, because no syntax errors will be reported and entire lines of code could be skipped.  The “On Error Resume Next” statement should never be used as a catch all for error handling.  Doing so will ultimately introduce more troubleshooting than it is worth. 

 

The bottom line on Bad Code, is that you WANT to find it, you NEED to find it, so only use “On Error Resume Next” on isolated code parts where 1-10 lines of code are written then immediately followed by “On Error Goto 0” to disable the error handling.  This practice is directly related to the Misconfigured Computer, and does not necessarily apply to Bad Data.

 

Bad Data

Bad Data simply means that the data you expected is not the data you received.  When this happens your script usually crashes with a run time error or you get unexpected results.  In these cases “On Error Resume Next” is usually of little benefit.  Why?  Well, your expectation of the data is wrong, proven by the fact you received incorrect data.  Furthermore, depending on the source of the data, you probably need to further investigate the data that is being supplied.  In some cases using “On Error Resume Next” will simply ignore the “bad data”, which could be the wrong thing to do.

 

The best way to handle any expectation of data is to check the data before you manipulate it.  This can usually be handled with simple “If” or “Select” statements.  By checking the data you can also report it back to the user in a graceful manner, letting them know what lines of data were incorrect.  This allows the user to decide how to deal with the issue.

 

Examples of Handling Bad Data and avoiding Errors

ReturnCaseComparisonValue

In this function if blnCase is not a Boolean (bad data) then a default value is used.  No error is needed, although one could be provided.

 

ResizeOnAdd

In this function several checks take place at different points, without the use of “On Error Resume Next”.   IsEmpty and IsArray are used to validate the aryAny in a specific way.  If the results indicate the aryAny is not the correct data type an error message can be returned to the user.

 

Also, in this function I use a favorite trick of mine (“If blnVerbose Then”).  In this case blnVerbose is a variable meant to be a Boolean (True or False).  However, if blnVerbose is Empty, a string, a date, etc, the statement is evaluated as False.  This can be viewed as another example of a “default” value being used when an incorrect value is supplied.

 

When returning a general error message always include the procedure name.  This allows for easier troubleshooting if the error is unexpected.

 

SequentialSearch         

A key component of this function is returning a default value of Empty when no match is found.  The use of Empty as a default return value is very useful for the following reasons.  It’s easy to check for using the IsEmpty function and it doesn’t introduce runtime errors when used in VBScript built-in functions that deal with strings and number (LCase, UCase, CInt, etc)

 

Misconfigured Computer

This is where “On Error Resume Next” really comes in handy.  A Misconfigured Computer does not have the COM object (or it is not working) that your script is attempting to use.  In VBScript any time you use the CreateObject() statement you are attempting to create a COM object on the computer.  However, that COM object must be properly defined on the computer, if it is not then a run time error will be encountered.

 

In simplistic terms when creating objects you should use the following code to gracefully handle misconfigured computers.

 

  On Error Resume Next

  Set objName = CreateObject("Object.Name")

  If Err.Number <> 0 Then

    On Error Goto 0

    'Do other error handling

  Else

    On Error Goto 0

    'Do required tasks

  End If 'CommonErrorHandler

  On Error Goto 0

 

On Error Goto 0” is used in several places to so that Bad Code (Syntax errors) are not introduced in the error handling routine.   This limits the effect of “On Error Resume Next” to only the “Set” and “If” statements.  All other code will be subject to correct syntax.

 

A more advanced method of Error Handling is to write a common error handling function that can be called in most situations and perform a number of actions like, return an error message and number, procedure name, etc.  Below is a version of the CommonErrorHandler function that I created.

 

'********************************************************************

'* Function CommonErrorHandler()

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2007-01-02

'* Modified: 2007-01-02

'*

'* Purpose: Displays Common Error Information, plus a given string.

'*          Return True if an error occured, to allow further error

'*          handling at the calling procedure.

'*

'*   Input: strText: A string of Text to be printed that completes

'*                    the sentence "An error occurred ..."

'*      strProcName: The calling prodedure name for debug purposes.

'*         intError: The errorlevel code to return when exiting. (Optional)

'*          blnQuit: A boolean value that incates to quit the script or not.

'*                   True = Quit Script

'*                   False = Continue

'*

'*  Output: Returns a Boolean indicating if and error occured.

'*            True  = An error occurred

'*            False = No error occurred

'*

'*   Notes: Clears the error before returning.

'*

'* Changes:

'********************************************************************

Function CommonErrorHandler(ByVal strText, ByVal strProcName, ByVal intError, _

                            ByVal blnQuit)

  'Version: 1.0 2007-01-02

  Dim strError, strNow, strDesc

 

  CommonErrorHandler = False

  If Err.Number <> 0 Then

    If IsEmpty(Err.Description) Or Err.Description = "" Then

      strDesc = "Type 'Net Helmsg " & (Err.Number - &H80070000) & _

                "' for more information"

    Else

      strDesc = Err.Description

    End If 'IsEmpty(Err.Description) Or Err.Description = ""

    strNow = Now

    strError = strNow & ": An error occurred " & strText & vbCrLf

    strError = strError & Space(Len(strNow)+2) & "0x" & Hex(Err.Number) & _

               " - " & strDesc & vbCrLF

    strError = strError & Space(Len(strNow)+2) & "Procedure: " & strProcName

    WScript.Echo strError

 

    If blnQuit Then

      WScript.Quit(intError)

    End If 'blnQuit

    CommonErrorHandler = True

  End If 'Err.Number <> 0

  Err.Clear

End Function 'CommonErrorHandler

 

After creating the CommonErrorHandler our code for basic error handling now looks like this.

  On Error Resume Next

  Set objName = CreateObject("Object.Name")

  If CommonErrorHandler("while creating Object.Name.", "ProcName",  3, True) Then

    On Error Goto 0

    'Do other error handling

  Else

    On Error Goto 0

    'Do required tasks

  End If 'CommonErrorHandler

  On Error Goto 0

 

The CommonErrorHandler is most commonly used when dealing with objects in scripts, and is best suited for reporting errors to the screen.   An example of this is provided in the Simplifying Object (FileSystemObject) Access section below.  If you need to check for an error and not report it, stick with the “If Err.Number <> 0 Then” syntax listed earlier.

 

Simplifying Object (FileSystemObject) Access

All Object access in VBScript is handled through the use of COM objects.  When dealing with COM objects the functions and procedure we create should fall into the following categories.

  • Initialize the object
  • Inspect parts of the object
  • Work with the object
  • Close the object

 

In this example we will be working with the “Scripting.FileSystemObject” COM object.  The first step in working with COM objects is to gain some understanding of its methods and properties, and what use you can make of them.  The use of the FileSystemObject (FSO) should be pretty straight forward from this standpoint.  When working with files and folders we need the following ability, regardless of how it is implemented in the object itself.

 

Inspection

Reads

Writes

Deletes

Create

File Exists

Read From File

Write To a File

Delete File

Initialize FSO

Folder Exists

Read folder contents

Move File

Delete Folder

Create File

 

 

Move Folder

 

Create Folder

 

This is not a complete list of items for an FSO implementation, but it does provide a general guide.

 

The first step in dealing with any COM object in VBScript is to create it.  I term this process Initialize, and I create a specific function for each object that will handle all the basics configuration of the object and any needed error checking anytime I create the specific COM object.  Below is the FSO initialization function I created.

 

'********************************************************************

'*

'* Function fsoInitialize

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2007-01-02

'* Modified: 2007-01-02

'*

'* Purpose: Create the File System Object (FSO) and check for errors.

'*

'* Input:    objFSO: The File System Object name to return.

'*         intError: The errorlevel code to return when exiting. (Optional)

'*          blnQuit: A boolean value that incates to quit the script or not.

'*                   True = Quit Script

'*                   False = Continue

'*

'* Output:  Return a Boolean indicating if the FSO was created successfull.

'*            True  = Success creating FSO

'*            False = Error creating FSO

'*

'* Calls:

'*  CommonErrorHandler

'*

'* Changes:

'* 2007-01-02: Replaced fsoErrorHandler with CommonErrorHandler

'********************************************************************

Function fsoInitialize(ByRef objFSO, ByVal intError, ByVal blnQuit)

  'Version: 1.1 2007-01-02

  On Error Resume Next

  Set objFSO = CreateObject("Scripting.FileSystemObject")

  If CommonErrorHandler("while creating FileSystemObject.", "fsoInitialize", _

                          intError, blnQuit) Then

 

    fsoInitialize = False

  Else

    fsoInitialize = True

  End If 'CommonErrorHandler

  On Error Goto 0

End Function 'fsoInitialize

 

The next step for working with COM objects using VBScript is to become familiar enough with it so you can categorize basic functionality it groups like we have for the FSO.  This should primarily focus on the functionality you need for the current project.  While you should always understand the benefits of developing a complete set of functions for the COM object you are working with, don’t get bogged down in developing all the functions at one time, when only a few are needed.  You can always come back and add the other functions later.

 

After you have categorized the functionality that you need focus on the Inspection functions first, these are not always easily identified, but may come up as you start writing a higher level function.  For example the items listed in the Inspection category of the FSO are needed for just about every file or folder access.  For example we must determine if the file or folder already exists before we can create it, read from it, delete it, move it, etc.

 

Below is the fsoFileExists function that I have created.  You will notice that since the FSO object is already created all you need to do is pass the object (objFSO) by reference (ByRef) to each procedure your create.  You can then pass any other parameters that are needed for the specific function being written.

 

'********************************************************************

'*

'* Function fsoFileExists

'*

'*   Author: NetworkAdminKB.com

'*  Created: 2007-01-02

'* Modified: 2007-01-02

'*

'* Purpose: Determines if a file exists given its full path, relative

'*            path, or just the file name.

'*

'* Input:   objFSO = The File System Object

'*    strFilePath  = A string indicating the file to check.

'*

'* Output:  Return a Boolean indicating if the file exists.

'*            True  = File Exists

'*            False = File Does NOT Exist

'*

'********************************************************************

Function fsoFileExists(ByRef objFSO, ByVal strFilePath)

  'Version: 1.0 2007-01-02

   If (objFSO.FileExists(strFilePath)) Then

     'Return True if the file exists

      fsoFileExists = True

   Else

      'Return False if the file does not exists

      fsoFileExists = False

   End If '(objFSO.FileExists(strFilePath))

End Function 'fsoFileExists

 

The next step would be to continue with developing functions that perform the specific tasks outlined above.  Focus on items that are needed for your specific project first.

 

Summary

Writing scripts as a Windows Administrator is usually a job requirement, but it is also a skill that a lot of administrators are afraid of, or lack all together.  Once you learn to write basic code you should try to implement the following suggestions into your skill set.

 

Refrain from writing your scripts in a linear method start to finish.  Instead write procedures and functions that do specific tasks as part of that program.  This will allow you to test and develop sections of code outside the program, and will aid the in development of a code library.

 

Writing functions that can simplify return values to True and False helps ease code development for other procedures that call those functions.  This allows simple “If” statements to be used to determine if the desired result was achieved by the called function.

 

Convert data into formats that are best suited for use with computers.  Having the ability to sort dates and times in your script can be very useful.

 

Implement default values that use the “best case” option for parameters that are being passed into your procedures.  This will allow your code to continue to work without complicated error handling.

 

Use Empty as a return value for functions when the desired return result was not found, created, or an error occurred.

 

Create initialize functions for all COM object creations that can handle configuring standard parameters and error checking.  This will provide consistency in your programs and reduce the time it takes to development future projects.

 

Refrain from using “On Error Resume Next” as a general error handler in your scripts.  Instead use it in a targeted fashion, disabling it as soon after the error as possible.

 

Create and use a standard error handling routine that can be used to report error messages back to the screen.  This will simplify debugging in the future and will simplify development of future projects.

 

Develop a large strings function library as it will be a cornerstone of most future projects.

 

Develop a set of functions for working for common interfaces like Arrays, Objects, etc.  Create functions that simplify the normally complicated interface.  This will allow you to more easily implement these type interfaces into your projects.

Article ID: 3, Created On: 9/15/2011, Modified: 9/15/2011