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