Training
Certifications
Books
Special Offers
Community




 
Microsoft® Windows® 2000 Scripting Guide
Author The Microsoft Windows Resource Kit Scripting Team
Pages 1328
Disk 1 Companion CD(s)
Level Int/Adv
Published 12/16/2002
ISBN 9780735618671
Price $49.99
To see this book's discounted price, select a reseller below.
 

More Information

About the Book
Table of Contents
Sample Chapter
Index
Related Series
Related Books
About the Author

Support: Book & CD

Rate this book
Barnes Noble Amazon Quantum Books

 


Chapter 2: VBScript Primer continued


Arrays

Scripts are probably most useful when you need to carry out the same task multiple times. For example, if you own one computer, it is just as quick and just as easy to open the Services snap-in to see which services are installed as it would be to write a script to retrieve the same information. If you have 100 computers, however, you will likely prefer using a script to retrieve service information as opposed to manually retrieving this information.

Of course, if a script is to run against 100 different computers, it needs a way to store those computer names and a way to keep track of which computers it has retrieved service information from and which computers it has not. One common way to store related bits of information (such as a list of computer names) is in an array, a special type of variable that can be used to store (and later retrieve) multiple values.

Creating Arrays

The easiest way to create an array is to use the Array function. As easy as this is, however, there are two things to keep in mind when using the Array function. First, the Array function can be used only to create one-dimensional arrays. Second, this function requires you to know in advance each of the items to be placed in the array. In system administration tasks that usually is not the case: After all, one of the primary reasons you write a script is to determine these items (which services are installed on a computer, which computers are in a specified organizational unit, which printers are being managed by a particular print server. Only then could you place them in an array.

Nevertheless, at times you will know the items in advance, and thus will be able to use the Array function. For example, the script used in the first half of this chapter assumed that you needed to run the script against exactly three computers. This might occur in your organization as well: If you have eight e-mail servers, you might want to hard-code all eight computer names into a script.

To put items in an array, use the Array function. With this function, you simply assign a list of values to a variable. For example, the following code statement assigns four domain controllers to an array variable named Computers:

Computers = Array("atl-dc-01", "atl-dc-02", "atl-dc-03", "atl-dc-04")

Declaring and Populating Arrays

Of course, there is a very good chance that you will not know, in advance, the items to be stored in an array. For example, your script might read in computer names from a text file, or might populate an array using the names of files found in a folder. In either case, you will not know the actual array items, or even how many items there are, until the script is run.

VBScript allows you to designate a variable as being an array without using the Array function and without immediately populating that array. To do this, you must use a Dim statement and must indicate the maximum number of items the array can hold. This can be a little confusing because the dimension of the array is based on the highest allowed index number rather than the actual number of items. Because the first item in an array is actually item 0 rather than item 1, the maximum number used in the Dim statement must always be the maximum number minus 1. If your array will contain 9 items, the Dim statement must show the maximum number as 8 (9 - 1).

For example, this command creates an array variable named arrTestArray and indicates that the array can contain a maximum of 4 items (4 - 1 = 3):

Dim arrTestArray(3)

After you have declared an array, you can populate it by assigning a value to the appropriate item. For example, this line of code assigns the value "A" to item 0, the first item in the array:

arrTestArray(0) = "A"

This simple script creates an array named arrTestArray and assigns values to each of the four items:

Dim arrTestArray(3)
arrTestArray(0) = "A"
arrTestArray(1) = "B"
arrTestArray(2) = "C"
arrTestArray(3) = "D"

So far, so good. But what if you guessed wrong when originally declaring the array? What if arrTestArray will actually contain five items rather than four? If that is the case, an error will be generated when you try to assign a fifth item to the array; an array cannot contain more items than the maximum number assigned to it. For example, this sample script creates a four-item array and then attempts to assign a value to a fifth item:

Dim arrTestArray(3)
arrTestArray(0) = "A"
arrTestArray(1) = "B"
arrTestArray(2) = "C"
arrTestArray(3) = "D"
arrTestArray(4) = "E"

When the preceding script runs, a "Subscript out of range" error is generated.

In a situation such as this, you have two options:

  • You can allocate more space to the array than you will need. For example, you might declare, in advance, that arrTestArray will contain a maximum of 1,000 items. That provides adequate storage space for the array. However, it also wastes memory, and hundreds of items in the array might have no value at all.
  • You can create a dynamic array.

Creating Dynamic Arrays

A dynamic array offers two advantages to script writers:

  • You do not have to specify the maximum size of the array in advance.
  • The size of the array can change during the course of the script.

For example, you might have a script that reads computer names from a text file. Rather than guess at the number of names in the text file, you can use a dynamic array to store these names. That way, you can create an array, read in the first name, and store it as the first item in the array. If a second name happens to be in the file, you can resize the array and then read in and store the second name. You can then repeat this process until every name has been read and stored.

To create a dynamic array, you must do two things. First, when you declare the array, do not specify a maximum size. Instead, simply use empty parentheses, like this:

Dim arrTestArray()

Second, before you add a new item to the array, use the ReDim Preserve statement and increment the size of the array by 1. The ReDim Preserve statement performs two tasks:

  • The ReDim portion resizes the array.
  • The Preserve portion ensures that no data is lost when the array is resized. This is important. If you simply use the ReDim statement by itself, all the data in your array will be lost when the array is resized.

For example, the following code sample creates a dynamic array named arrTestArray. The script creates a variable named intSize and assigns this the value 0. The script then uses the resultant line of code (intSize = 0) to resize arrTestArray so that it accepts one element (1 - 1 = 0).

After the script has assigned a value to the first element (item 0) in the array, ReDim Preserve is used to resize the array so that it accepts two elements (intSize + 1). A value is then assigned to the second element:

Dim arrTestArray()
intSize = 0
ReDim Preserve arrTestArray(intSize)
arrTestArray(intSize) = "A"
ReDim Preserve arrTestArray(intSize + 1)
arrTestArray(intSize + 1) = "B"

To illustrate how you might use ReDim Preserve in an actual system administration script, the following code sample retrieves a list of all the services installed on a computer and stores those service names in a dynamic array. To perform this task, the script must:

  1. Create a dynamic array named arrTestArray.
  2. Create a variable named intSize and assign this variable the value 0.
  3. Connect to the WMI service and retrieve a list of the installed services.
  4. For each service in the returned collection:
    1. Use the ReDim Preserve statement to set the size of arrTestArray to the value stored in intSize. For the first service in the collection, intSize is equal to 0. The array arrTestArray will thus have a size of 0, meaning it can contain one element.
    2. Assign the name of the service (objService.DisplayName) to the array element just created.
    3. Increment the size of intSize. For example, after the first element has been assigned a value, intSize will be changed to 1 (the original value, 0, plus 1).
    4. Repeat the step for the remaining services in the collection. For the second service, ReDim Preserve sets the size of the array to 1, allowing two elements to be contained within the array. For the third service, ReDim Preserve sets the size to 2, allowing three elements to be stored in the array.
Dim arrTestArray()
intSize = 0

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery _
("SELECT * FROM Win32_Service")

For Each objService in colRunningServices
ReDim Preserve arrTestArray(intSize)
arrTestArray(intSize) = objService.DisplayName
intSize = intSize + 1
Next

Converting a Delimited String to an Array

To make administrative data readily accessible to a wide variety of applications (including scripts), this data is often saved in plain-text files and in delimited format. Delimited simply means that a unique character separates the individual fields for each record in the file. This character is known as a delimiter and is typically the comma. These files are often referred to as comma-separated-values files because the individual values are separated by commas.

For example, a log file that lists the name of a server, the name of an event log on that server, and the number of error events that were recorded in that event log might look like this:

atl-dc-01,DNS Server,13
atl-dc-01,System,14
atl-dc-02,Security,3
alt-dc-02,Application,22

One advantage of delimited files is that they can be imported into applications that can automatically parse each line into a single record with multiple fields. For example, an application such as Microsoft Excel or Microsoft Access would parse the sample file like the example shown in Table 2.18.

Table 2.18   Parsing a Sample Data File into Fields

Field 1Field 2Field 3
atl-dc-01DNS Server13
atl-dc-01System4
atl-dc-02Security3
atl-dc-02Application22

The Split function can be used to convert a delimited string to an array. This is done by passing the function two parameters:

  • The delimited text string.
  • The delimiter.

For example, in the following script the delimited text string is the variable TestString, and the comma serves as the delimiter:

TestString = " atl-dc-01,DNS Server,13"
TestArray = Split(TestString , ",")
For i = LBound(TestArray) to UBound(TestArray)
Wscript.Echo TestArray(i)
Next

When this script is run under CScript, the individual data fields are echoed to the screen:

atl-dc-01
DNS Server
13

Alternatives to Using Arrays

Arrays provide a quick and easy way to track one-dimensional data. When you have multidimensional data, however, it can become difficult to keep track of the various subscripts; it can also become exceedingly complex to try to sort the data. Because of that, if you do have multidimensional data you might want to consider one of the these alternatives:

  • Dictionary object. The Dictionary object is part of the Script Runtime library and provides a way for you to easily store, retrieve, and modify array-like data. For more information about the Dictionary object, see "Script Runtime Primer" in this book.
  • Disconnected recordset. A disconnected recordset is a temporary database that is not tied to an actual physical database. Instead, the recordset is constructed in memory and is deleted when the script finishes. Like the Dictionary object, the disconnected recordset allows you to refer to items by name rather than by an index number. Equally important, a disconnected recordset has access to the same methods and properties as a standard recordset, including the ability to sort records. For example, the following code sample sorts a one-dimensional array:
  • For i = LBound(SampleArray) to UBound(SampleArray)
    For j = LBound(SampleArray) to UBound(SampleArray)
    If j = UBound(SampleArray) Then
    Else
    If SampleArray(j) > SampleArray(j + 1) Then
    TempValue = SampleArray(j + 1)
    SampleArray(j + 1) = SampleArray(j)
    SampleArray(j) = TempValue
    End If
    End If
    Next
    Next

    By contrast, this single line of code sorts a disconnected recordset by field name (in this case, FileSize):

    DisconnectedRecordset.Sort = "FileSize"

    For more information about disconnected recordsets, see "Creating Enterprise Scripts" in this book.

Error Handling

VBScript provides a relatively simple method for handling run-time errors within a script. Run-time errors represent any errors that are not caught by the compiler and thus manifest themselves only after the script has started running. For example, the following script generates a syntax error because the command Wscript.Echo is mistyped and the compiler is unable to interpret the line. Before a script actually runs, the scripting engine reads each line to verify that the syntax is correct; no lines of code are actually run until this checking verifies that the syntax of all the lines of code is correct. Because of that, an error message will be displayed, even though the misspelled command does not occur until line 3 in the script:

Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
W script.Echo "Line 3."

Instead of the message boxes for lines 1 and 2, the error message shown in Figure 2.20 appears. This error is generated because line 3 appears to reference an invalid command named W.

Click to view graphic
Click to view graphic

Figure 2.20   Syntax Error Message

By contrast, the following script will display two message boxes before encountering an error (the misspelling of Wscript.Echo). This is because, as far as the compiler is concerned, line 3 is typed correctly. The compiler has no way of knowing that Eho is not a valid Wscript property and thus does not flag this as an error. VBScript cannot determine the properties and methods of a COM object in advance (that is, before a particular line of code runs).

Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
Wscript.Eho "Line 3."

When line 3 of the script is actually run, the error message shown in Figure 2.21 appears. You might notice that this error message lists the source as a Microsoft VBScript run-time error rather than as a compilation error.

Click to view graphic
Click to view graphic

Figure 2.21   Runtime Error Message

Not all run-time errors involve typographical errors within the script. For example, this line of code is syntactically and typographically correct. However, it will also generate a run-time error if the remote computer named InaccessibleComputer is not available over the network:

Set Computer = GetObject("winmgmts:\\InaccessibleComputer")

This means that run-time errors are bound to occur, if only because of activities beyond your control. (The network goes down, a computer is shut down, another administrator deletes a user account.) With VBScript, you have three options for handling errors. The advantages and disadvantages of each of these options are summarized in Table 2.19.

Table 2.19   Advantages and Disadvantages of Error Handling Options

OptionAdvantagesDisadvantages
Allow the script to fail.
  • Requires no work of any kind on the part of the script writer. By default, all scripts will fail when they encounter a run-time error.
  • In many cases, running part of a script might be worse than not running a script at all. For example, if you have a script that backs up files to a remote computer and then deletes the original files, you would probably prefer that the script fail if the backup cannot take place. You would not want the backup portion to fail but then have the delete portion succeed.
  • Allowing the script to fail at some arbitrary point could leave your computer in an unstable state.
  • Some failures need not be fatal. For example, suppose a monitoring script is designed to retrieve information from 100 computers. If computer 1 is inaccessible, the script will fail and no information will be returned, even though the other 99 computers are up and running.
Have the script ignore all errors.
  • Scripts will continue to run without interruption.
  • Scripts can complete a major share of their tasks, even if a problem arises. For example, a script might be able to successfully copy 99 of 100 files, even if one file could not be copied.
  • End users will not be presented with error messages that they must respond to.
  • Difficult to debug problems because no message is displayed indicating that an error occurred, nor is any clue given as to where the error might have taken place.
  • Can leave a computer in an uncertain state, wherein some actions have been carried out while others have not.
Write code to respond to errors as they occur.
  • Allows you to create meaningful error messages.
  • Allows you to create code that attempts to address the error that occurred. For example, if a script is unable to connect to a remote computer, it might try to make that connection again before terminating.
  • Requires extra work on the part of the script writer. The script writer must anticipate where errors are likely to occur and then include the code to respond appropriately.

Handling Run-Time Errors

For the most part, to handle a run-time error simply means, "If an error occurs, do not allow the script to fail, but do something else instead." Typically the "something else" that you can do is one of the following:

  • You can ignore the error and proceed with the next line of the script.
  • You can trap the error and include code that responds to the error in some way (for example, by displaying an error message to the user).

Both of these methods for handling errors are implemented by including the On Error Resume Next statement within the script. When running under On Error Resume Next, a script will not fail when encountering a run-time error. Instead, the script will ignore the line of code where the error occurred and will attempt to process the next line of code. This will continue until every line of code has been either ignored (because an error occurred) or processed.

Ignoring All Errors

By far the simplest form of error handling is the type that instructs a script to ignore all errors and continue running until every line of code has been processed. To create a script that ignores all errors and continues to run, place the On Error Resume Next statement at the beginning of the script. For example, the following script attempts to use a nonexistent WSH method (Wscript.X); none of the lines of code can thus run successfully. However, the script will actually run without generating an error message because of the On Error Resume Next statement:

On Error Resume Next
Wscript.X "Testing 1-2-3."
Wscript.X "Testing 4-5-6"
Wscript.X "Testing 7-8-9"

Note that error handling does not begin until VBScript processes the On Error Resume Next statement. For example, the following script will generate a run-time error because On Error Resume Next is not implemented until the error has already occurred:

Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
Wscript.Eho "Line 3."
On Error Resume Next
Wscript.Echo "Line 4."

To ensure that error handling is in place before an error occurs, put the On Error Resume Next statement near the beginning of your script:

On Error Resume Next
Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
Wscript.Eho "Line 3."
Wscript.Echo "Line 4."

When the preceding script is run under CScript, the following output appears in the command window. When the script host encountered the run-time error generated by Wscript.Eho, it simply skipped that line and continued with the rest of the script:

Line 1.
Line 2.
Line 4.

Responding to Errors

Instead of having your script ignore errors, you can create code that periodically checks the error condition and then takes some sort of action. For this purpose, you might create code that:

  1. Checks to see whether the value of the Err object is not equal to 0. (If Err is equal to 0, that means that no run-time error has occurred.)
  2. Takes some sort of action (for example, displaying the value of Err.Number and Err.Description).
  3. Resets the value of the Err object to 0. This is very important because, as noted previously in this chapter, the Err object does not automatically reset itself each time the error condition is checked.

For example, the following script sample first attempts to connect to the WMI service. If this connection attempt fails (that is, if Err does not equal 0), a message box is displayed showing the error number and description. After displaying the error message, the script then clears the Err object.

The script then attempts to return a list of all the services installed on a computer. This line of code will fail because the ExecQuery method has been misspelled as ExcQuery. After running this line of code, the script will again check the error condition. If Err does not equal 0, a message box will be displayed showing the error number and description. After displaying the error message, the script then clears the Err object.

On Error Resume Next
Set Computer = GetObject("winmgmts:")
If Err <> 0 Then
Wscript.Echo Err.Number & " -- " & Err.Description
Err.Clear
End If
Set ServiceList = Computer.ExcQuery("SELECT * FROM Win32_Service")
If Err <> 0 Then
Wscript.Echo Err.Number & " -- " & Err.Description
Err.Clear
End If

When this script is run under WScript, the message box in Figure 2.22 is displayed:

Click to view graphic
Click to view graphic

Figure 2.22   WMI Error Message

Toggling Error Handling

At times you might want to implement error handling in one portion of a script but not in another portion. In other words, sometimes you might want to trap errors, while other times you might prefer to simply let the script fail.

You can toggle error handling on and off by:

  • Using the On Error Resume Next statement to turn error handling on.
  • Using the On Error GoTo 0 statement to turn error handling off. After this statement has been run, error handling will not take place until another On Error Resume Next statement has been encountered.

For example, in the following script, error handling is implemented in the first line. In the second line, an error occurs because Wscript.Echo has been misspelled. However, because error handling has been implemented, the script will continue to run. Error handling is turned off in line 3, using On Error GoTo 0, and then reimplemented in line 5. Because no errors occur while error handling is off, the script will run without failing.

On Error Resume Next
Wscript.Eco "A"
On Error GoTo 0
Wscript.Echo "B"
On Error Resume Next
Wscript.Eco "C"
Wscript.Echo "D"

When the preceding script is run under Cscript, the following data is displayed in the command window. The values "A" and "B" are the only values echoed to the screen because those are the only lines of code that could be run without generating an error.

A
B

The following sample script shows a slightly revised version of the same script. In this case, error handling is turned on and then off, but this time an error occurs while error handling is off.

On Error Resume Next
Wscript.Eco "A"
On Error GoTo 0
Wscript.Eco "B"
On Error Resume Next
Wscript.Eco "C"
Wscript.Echo "D"

When this script is run under Cscript, the following error message is displayed in the command window:

C:\Documents and Settings\gstemp\Desktop\Scripts\x.vbs(4, 1) Microsoft VBScript
runtime error: Object doesn't support this property or method: 'Wscript.Eco'

In this case, the script fails to run line 2 (with the misspelling of Wscript.Echo) but continues to run because error handling has been implemented. In line 3, however, error handling been turned off. As a result, the misspelling in line 4 causes the script to fail, generating a run-time error and the resultant error message.

Handling Errors in COM Objects

One issue that complicates error handling is the fact that system administration scripts typically make use of COM objects. If an error occurs with the COM object itself, VBScript will be aware of the error. However, VBScript might have no knowledge of what actually caused the error. For example, in the following script, WMI attempts to bind to a nonexistent printer named TestPrinter. Not surprisingly, this raises an error. You might expect, therefore, to be able to trap the error number and description and echo that to the screen:

On Error Resume Next
Set Test = GetObject _
("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'")
Wscript.Echo Err.Number & VbTab & Err.Description

When the script runs, however, you get neither a standard four-digit VBScript error number nor any error description. Instead, you get the cryptic message box shown in Figure 2.23.

Click to view graphic
Click to view graphic

Figure 2.23   COM Object Error Number and Description

The problem is that the error occurred within WMI, and the error details are not available to VBScript. VBScript receives notice that an error occurred, and in most scripts that type of notice might be sufficient. In other scripts, however, or when you are developing and debugging a new script, you might find detailed error information much more useful.

Many COM objects provide their own mechanisms for trapping error information. For example, WMI allows you to create an error object, SWbemLastError, and then retrieve the following information:

  • Method being called when the error occurred.
  • Parameter responsible for generating the error.
  • Name of the WMI provider where the error occurred.

The following script uses the SWbemLastError object to retrieve error information from within WMI and then display that information in a message box.

On Error Resume Next
Set Test = GetObject _
("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'")
Set WMI_Error = CreateObject("WbemScripting.SwbemLastError")
Wscript.Echo WMI_Error.Operation & VbTab & _
WMI_Error.ParameterInfo & VbTab & WMI_Error.ProviderName

When the preceding script runs, the message box in Figure 2.24 appears.

Click to view graphic
Click to view graphic

Figure 2.24   Message Box Using SWbemLastError

When working with SWbemLastError, you must create the error object following the line of code where you believe an error could occur. For example, the following script will not return any error information because the SWbemLastError object was created before the error occurred:

On Error Resume Next
Set WMI_Error = CreateObject("WbemScripting.SwbemLastError")
Set Test = GetObject _
("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'")
Wscript.Echo WMI_Error.Operation & VbTab & _
WMI_Error.ParameterInfo & VbTab & WMI_Error.ProviderName

Procedures

The first few system administration scripts you write are likely to be simple scripts that carry out a single task and then stop. For example, you might have a script that connects to a computer, retrieves a list of the services running on that computer, and then ends. You might have a second script that connects to a computer and starts a selected set of services, and a third that connects to a computer and stops a selected set of services.

As you become more proficient with scripting, you might begin to write more complex scripts, perhaps by combining several simple scripts. Instead of having three separate scripts for managing services, you might create a single service management script that can perform three different tasks depending on the command-line parameters entered. For example:

  • If you include /info on the command line, the script retrieves service information.
  • If you include /start on the command line, the script starts a selected set of services.
  • If you include /stop on the command line, the script stops a selected set of services.

As your scripts become more complex, you might find it beneficial to place portions of the code within a procedure. Procedures are lines of code that carry out a specific task. For example, your service management script might have three procedures: one to retrieve service information, one to start selected services, and one to stop selected services. Placing code within procedures makes it easy to identify and to isolate the tasks carried out by the script. This enhances the readability and maintainability of the script and also makes it easy to copy and paste the code into scripts that carry out similar activities.

Procedures are also useful for scripts that need to carry out the same task over and over. For example, you might want to echo a message each time an error occurs within a script. To do this, you can include the message display code at every point in the script where an error might occur. Of course, this requires extra work, not only to write the original code but to maintain it as well; if you ever decide to change the displayed message, you will have to locate and change each instance within the code.

A better approach is to create a procedure that displays the message and then call that procedure each time an error occurs. As a result, you have to write — and maintain — only a single instance of the display code.

VBScript allows you to create two types of procedures:

  • Subroutines. Code that is run but typically does not return a value. For example, you might create a subroutine to display error messages.
  • Functions. Code that is run and returns a value. Functions are often used to carry out mathematical equations. For example, you might pass free disk space to a function, and the function will, in turn, convert the free space from bytes to gigabytes and then return that value.

Calling a Procedure

In general, VBScript processes each line of code in succession. Procedures are an exception to this rule. Neither subroutines nor functions are run unless they have been specifically invoked somewhere in the script. If a procedure has not been invoked, the procedure is simply skipped, regardless of where it appears within the script.

For example, the following script includes a subroutine embedded in the middle of the code. However, this subroutine is never actually called.

Wscript.Echo "A"
Sub EchoLine2
Wscript.Echo "B"
End Sub
Wscript.Echo "C"

When the preceding script runs under CScript, the following output appears in the command window. Because the subroutine was never called, the subroutine and all the code inside it was skipped and not run:

A
C

The flow of the script goes like this:

  1. The script runs line 1 and echoes the message "A".
  2. The script parses line 2 and sees that it marks the beginning of a subroutine. Because the subroutine has not been called, the script skips lines 2, 3, and 4.
  3. The script runs line 5, the first line following the end of the subroutine, and echoes the message "C". The script then automatically stops without ever running the subroutine.

To ensure that a subroutine runs, it must be called. This is done using a statement that consists solely of the subroutine name. For example, the following script echoes the message "A", calls the subroutine named EchoLineB, and then echoes the message "C".

Wscript.Echo "A"
EchoLine2
Wscript.Echo "C"
Wscript.Quit
Sub EchoLineB
Wscript.Echo "B"
End Sub

When the preceding script runs under CScript, the following output appears in the command window:

A
B
C

The flow of the script goes like this:

  1. The script runs line 1, and echoes the message "A".
  2. The script runs line 2, which is a call to the subroutine named EchoLineB.
  3. The script skips to line 5, where the subroutine begins. It skips all the intervening lines, including line 4, which would have caused the script to stop.
  4. The script runs line 6, the only line within the subroutine, which echoes the message "B".
  5. The script runs line 7, which marks the end of the subroutine. Because the subroutine has ended, control of the script returns to the line immediately following the line (line 2) where the subroutine was called.
  6. The script runs line 3 and echoes the message "C".
  7. The script runs line 4 and stops.

Procedures can be placed anywhere within a script, with no degradation of performance. However, the placement of procedures can affect the ease with which a script can be read and maintained. For more information about placing procedures within a script, see "Scripting Guidelines" in this book.

In the following script, an error-handling procedure is used to display any WMI errors that might occur. Throughout the script the Err object is checked. If the value is anything but 0, this means an error occurred; the ErrorHandler subroutine is called, and the appropriate error message is displayed.

On Error Resume Next
Set objWMIService = GetObject("winmgmts:root\cimv2")
If Err <> 0 Then
ErrorHandler
End If
Set colPrinters = objWMIService.ExecQuery _
("SELECT * FROM Win32_Printer WHERE Name='TestPrinter'")
If Err <> 0 Then
ErrorHandler
End If
For Each objPrinter in colPrinters
Wscript.Echo objPrinter.Name
Next
Sub ErrorHandler
Select Case Hex(Err.Number)
Case "80041001"
Wscript.Echo "The call failed."
Case "80041002"
Wscript.Echo "The object could not be found."
Case "80041010"
Wscript.Echo "The specified class is not valid."
Case "8004103A"
Wscript.Echo "The specified object path was invalid."
Case "80041048"
Wscript.Echo "The specified class is not supported."
Case Else
Wscript.Echo "An unknown error occurred."
End Select
Err.Clear
End Sub

Functions

Like subroutines, functions provide a way for you to use one section of code multiple times within a script. Unlike subroutines, however, functions are designed to return a value of some kind. This is not necessarily a hard-and-fast rule; VBScript does nothing to ensure that a function always returns a value and that a subroutine never returns a value. However, the scripting language is designed to make it easier to return values using functions.

In fact, when you create a function, VBScript automatically declares and initializes a variable that has the same name as the function. This variable is designed to hold the value derived by the function. Although there is no requirement that you use this variable, doing so makes it very clear that the value in question was derived by the function of the same name.

For example, the following script includes the statement Wscript.Echo ThisDate. ThisDate also happens to be the name of a function that retrieves the current date. In this script, notice that:

  • The Wscript.Echo statement actually performs two tasks.
    • First it calls the function ThisDate. The function, in turn, sets the value of the special variable ThisDate to the current date.
    • After the function has completed, Wscript.Echo then echoes the value of this special variable.

  • Option Explicit is used, and the variable ThisDate is never declared. However, no error occurs because VBScript internally declares and initializes this function variable for you.

Option Explicit
Wscript.Echo ThisDate
Function ThisDate
ThisDate = Date
End Function

Note that this approach works only for a function, and not for a subroutine. The following code generates a run-time error because VBScript is unable to assign the date to the name of a subroutine:

Wscript.Echo ThisDate
Sub ThisDate
ThisDate = Date
End Sub

Passing Parameters to Functions

Functions are often used to carry out a mathematical equation and then return the result of this equation. For example, you might use a function to convert bytes to megabytes or convert pounds to kilograms.

For a function to carry out a mathematical equation, you must supply the function with the appropriate numbers. For example, if you want a function to add the numbers 1 and 2, you must supply the functions with those two values. The numbers 1 and 2 are known as parameters (or arguments), and the process of supplying a function with parameters is typically referred to as passing those values.

To pass parameters to a function, simply include those values in the function call. For example, this line of code calls the function AddTwoNumbers, passing the values 1 and 2:

AddTwoNumbers(1 , 2)

In addition to including the parameters within the function call, the function itself must make allowances for those parameters. This is done by including the appropriate number of variables in the Function statement. This line of code, for example, creates a function that accepts three parameters:

Function AddThreeNumbers(x, y, z)

If the number of parameters in the Function call does not match the number of parameters in the Function statement, an error will occur. For example, this script generates a "Wrong number of arguments" error. Why? Because two values are passed to the function, but the Function statement does not allow for any parameters:

x = 5
y = 10
Wscript.Echo AddTwoNumbers(x, y)
Function AddTwoNumbers
AddTwoNumbers = a + b
End Function

To correct this problem, include space for two parameters within the Function statement:

x = 5
y = 10
Wscript.Echo AddTwoNumbers(x, y)
Function AddTwoNumbers(a, b)
AddTwoNumbers = a + b
End Function

You might have noticed that the parameters used in the Function call (x and y) have different names from the parameters used in the Function statement (a and b). VBScript does not require the parameter names to be identical; if it did, this would limit your ability to call the function from multiple points within a script. (Although this would be possible, you would always have to assign new values to variables x and y before calling the function. This could be a problem if you did not want to assign new values to x and y.)

Instead, VBScript simply relies on the order of the parameters. Because x is the first parameter in the function call, the value of x is assigned to a, the first parameter in the Function statement. Likewise, the value of y, the second parameter in the Function call, is assigned to b, the second parameter in the Function statement.

To show how a function might be used in an actual system administration script, the following sample retrieves the amount of free disk space on drive C of a computer and then calls a function named FreeMegabytes. This function converts the free space from bytes to megabytes and returns that value. This new value is then echoed to the screen:

Set objWMIService = GetObject("winmgmts:")
Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'")
Wscript.Echo FreeMegaBytes(objLogicalDisk.FreeSpace)
Function FreeMegabytes(FreeBytes)
FreeMegabytes = FreeBytes / 1048576
FreeMegabytes = Int(FreeMegabytes)
End Function

Note when working with functions that each time VBScript sees the name of a function, it will attempt to call that function. This means that even though there is a special variable named, in this case, FreeMegabytes, you do not have access to that variable except when calling the function. For example, in the following script, the FreeMegaBytes function is called, and the free space displayed. In the next line, the script then attempts to echo the value of the FreeMegabytes variable.

Set objWMIService = GetObject("winmgmts:")
Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'")
Wscript.Echo FreeMegaBytes(objLogicalDisk.FreeSpace)
Wscript.Echo "Free space: " & FreeMegaBytes
Function FreeMegabytes(FreeBytes)
FreeMegabytes = FreeBytes / 1048576
FreeMegabytes = Int(FreeMegabytes)
End Function

When the script runs, the error message shown Figure 2.25 appears. This happens because VBScript does not echo the value of the FreeMegaBytes variable. Instead, it tries to call the function FreeMegaBytes. This call fails because the function requires you to supply the number of free bytes.

Click to view graphic
Click to view graphic

Figure 2.25   Error Message for Improperly Accessing a Function Variable

If you need to refer to the value derived from a function without calling that function, save the value in a separate variable. For example, in this script, the value returned from the FreeMegabytes function is saved in the variable AvailableSpace. This variable can be used at any time without calling the function.

Set objWMIService = GetObject("winmgmts:")
Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'")
AvailableSpace = FreeMegaBytes(objLogicalDisk.FreeSpace)
Wscript.Echo "Free space: " & AvailableSpace
Function FreeMegabytes(FreeBytes)
FreeMegabytes = FreeBytes / 1048576
FreeMegabytes = Int(FreeMegabytes)
End Function

Passing Parameters by Value or by Reference

The values passed to a function are rarely hard-coded into a script; instead, values are typically passed to a function by using a variable. For example, the following two lines of code set the value of the variable x to 100 and then pass the variable to a function named ModifyValue:

x = 100
Wscript.Echo ModifyValue(x)

The value of x at the time the function is called is 100. The value of x at the time the function finishes running depends on two things: whether the function actually modifies the value in some way and whether the function was called by value or by reference.

To explain the difference between passing variables by value or by reference, consider the following script, which sets the value of x to 100 and then, within the function itself, changes the value of x to 99:

x = 100
Wscript.Echo ModifyValue(x) & VbTab & x
Function ModifyValue(x)
ModifyValue = x / 25
x = 99
End Function

When the preceding script runs, the message box shown in Figure 2.26 appears. As you can see, the variable x was divided by 25 and then was reassigned the new value 99.

Click to view graphic
Click to view graphic

Figure 2.26   Assigning a New Value Within a Function

In many scripts, the fact that the function changed the value of x makes no difference. However, what if you need to use the original value of x later in your script? In that case, the fact that the function changed the value of x makes a very big difference.

By default, VBScript passes variables by reference. This means that the function receives a reference to the variable's location in memory, and thus performs its calculations using the variable itself. Depending on the function, this can change the value of the variable.

To ensure that your variables are not changed by a function, pass the variables by value. With this approach, VBScript does not pass a reference to the actual variable; instead, it merely passes the value of that variable. Because the function does not have access to the variable itself, it cannot change the value of that variable.

To pass a variable by value, include the ByVal keyword within the name of the function. For example, this script passes the variable x by value to the function ModifyValue:

x = 100
Wscript.Echo ModifyValue(x) & VbTab & x
Function ModifyValue(ByVal x)
ModifyValue = x / 25
x = 99
End Function

When the preceding script runs, the message box shown in Figure 2.27 appears. Notice that the value of the variable x remains unchanged, even though the function appears to have set the value of x to 99. What really happened is that the function set the value of a copy of x to 99. Within the function itself, x now equals 99. As soon as the function ends, however, this temporary copy of x disappears. Meanwhile, the real x has retained its original value.

Click to view graphic
Click to view graphic

Figure 2.27   Passing a Variable by Value

You can pass some variables to a function by value and other variables to the same function by reference (using the ByRef keyword). For example, this line of code passes the variable x by value and the variable y by reference:

Function TestFunction(ByVal x, ByRef y)

In this function, it is possible for the value of y to be changed after the function finishes. However, the value of x will remain unchanged.

Recursion

Recursion is a programming technique in which a subroutine or a function calls itself. You have probably seen photographs of a person staring into a mirror. In turn, his or her reflection is staring into a mirror located in the background, which reflects the person in the mirror staring into the mirror, and so on. This is the basic idea behind recursion: Subroutine A calls subroutine A, which calls subroutine A, which calls subroutine A, and so on.

Although the concept of recursion might seem a bit bizarre, it actually has important uses in system administration scripting. For example, suppose you want to enumerate all the files in a shared folder. You can easily connect to the shared root folder and list all the files, but what happens if the shared root folder has subfolders? And what happens if those subfolders contain other subfolders? Recursion allows you to enumerate all items and subitems, even if you have no advance knowledge of the folder structure.

For example, the folder structure shown in Figure 2.28 shows a root folder, Scripts, and main subfolders, Subfolder 1 and Subfolder 2. Each of those subfolders contains other subfolders.

(Image Unavailable)

Figure 2.28   Sample Folder Structure

To enumerate all the folders shown in this figure, you will have to start with the Scripts folder, return a list of subfolders, bind to each subfolder, return a list of subfolders within those folders, and so on.

To perform this task, you can use a script similar to the following. This script uses a function named ShowSubFolders that is called over and over until every folder and subfolder has been enumerated.

Set FSO = CreateObject("Scripting.FileSystemObject")
ShowSubfolders FSO.GetFolder("C:\Scripts")
Sub ShowSubFolders(Folder)
For Each Subfolder in Folder.SubFolders
Wscript.Echo Subfolder.Path
ShowSubFolders Subfolder
Next
End Sub

The function ShowSubFolders does the following:

  1. Retrieves a collection consisting of all the subfolders of the root folder, C:\Scripts. This collection has two items: Subfolder1 and Subfolder 2.
  2. Takes the first item in the collection, Subfolder 1, and echoes the folder path. It then uses the name of that folder as a parameter passed to itself. In other words, it now runs the function ShowSubFolders using Subfolder 1 as the parameter.
  3. Retrieves a collection consisting of all the subfolders of Subfolder 1. This collection has two items: Subfolder1A and Subfolder 1B.
  4. Takes the first item in the collection, Subfolder 1A, and echoes the folder path. It then uses the name of that folder as a parameter passed to itself. In other words, it now runs the function ShowSubFolders using Subfolder 1A as the parameter.
  5. Because Subfolder 1A has no subfolders, control passes to the next item in the collection, Subfolder 1B. The function calls itself using Subfolder 1B as the parameter.
  6. Because Subfolder 1B has no subfolders, the function has finishes recursing through Subfolder 1. It thus returns to the second item in the original collection (subfolders of C:\Scripts), and repeats the entire process.

When the script runs, the following output appears:

C:\scripts\Subfolder 1
C:\scripts\Subfolder 1\Subfolder 1A
C:\scripts\Subfolder 1\Subfolder 1B
C:\scripts\Subfolder 2
C:\scripts\Subfolder 2\Subfolder 2A
C:\scripts\Subfolder 2\Subfolder 2B
C:\scripts\Subfolder 2\Subfolder 2C

Recursion is an extremely powerful technique for exploring data stored in a tree structure, including Active Directory as well as the file system.

COM Objects

The Component Object Model (COM) provides a standard way for applications (.exe files) or libraries (.dll files) to make their functionality available to any COM-compliant application or script. That is the textbook definition of COM. What COM really does, however, is make it possible for nonprogrammers to write scripts for managing Windows operating systems. COM provides a mechanism for translating script code into commands that can be acted on by the operating system. Without COM, anyone hoping to automate system administration would have to master not only a high-level programming language such as C++ or Visual Basic but also all of the Windows Application Programming Interfaces (APIs). In effect, COM brings Windows programming to the masses.

COM components are files (typically .exe or .dll files) that contain definitions of the objects the component has available for use. (These definitions are known as classes.) When you create a COM object in a script (a process known as instantiation), you are creating an instance, or copy, of one of the classes contained within the COM component. After the instance has been created, you can then take advantage of the properties, methods, and events exposed by the object.

Objects that make their functionality available through COM are known as COM servers. Applications or scripts that make use of that functionality are referred to as COM clients. For example, when you write a script that uses WMI, WMI is the COM server and your script is the COM client. COM servers can be implemented in one of two ways:

  • Out-of-process servers. Out-of-process servers are typically implemented in executable files and run in a different process than the script. For example, when you start a script, an instance of Wscript.exe begins to run. If you next instantiate a Microsoft Word object, you are then working with two processes: Wscript.exe and the Winword.exe process in which the Microsoft Word object runs.
  • In-process servers. Libraries (.dll files) are known as in-process servers because they run in the same process as the application or script that called them. For example, when you call the FileSystemObject from within a script, no new process is created. This is because the FileSystemObject (which is found in the Scrrun.dll library) is an in-process server and thus runs in the same process as the script. In-process servers typically run faster than out-of-process servers because the operating system does not have to cross process boundaries (from the script process to the object process and then back) to access the object's methods and properties.

As noted previously in this chapter, VBScript works with a subset of objects known as Automation objects. All COM objects must support one or more interfaces, which are simply the avenues by which a COM client can access the COM server. Any object that supports the IDispatch interface is known as an Automation object. Because not all COM objects support the IDispatch interface, VBScript cannot access all of the COM objects on your computer.

The COM Process

As a script writer, you have to know only how to create a reference to an Automation object. You do not have to worry about how to locate and load the object because the Windows operating system takes care of that for you. Nevertheless, it is still useful for you to understand what happens between the time when the script runs your CreateObject command and the time when the object itself is available for use.

What is especially useful to understand is that there is no magic here; in fact, the process is relatively simple. When you install an application or a library that contains object classes, that application or library registers itself with the operating system, a procedure that enables the operating system to know:

  • That a new COM server is available for use.
  • The object classes that the new COM server makes available.

The registration process includes adding a number of subkeys to HKEY_CLASSES_ROOT in the registry. Among the subkeys that are added is one that specifies the Programmatic Identifier (ProgID) for each new object class. The ProgID is a short text string that identifies the name given to each object class. In addition, the ProgID is the parameter used in the CreateObject and GetObject calls to specify the object you want to create. For example, in the following line of code, the ProgID is Excel.Application:

Set TestObject = CreateObject("Excel.Application")

The ProgID is all the information the operating system needs to locate and instantiate the COM object.

Creating a New Object

When a CreateObject call runs, the script engine parses out the ProgID and passes that to a COM API. This API actually creates the object reference. For example, in this line of code, the string Scripting.FileSystemObject is passed to the COM API:

Set TestObject = CreateObject("Scripting.FileSystemObject")

The COM API searches the HKEY_CLASSES_ROOT portion of the registry for a subkey with the same name as the ProgID. If such a subkey is found, the API then looks for a subkey named CLSID.

The CLSID subkey maintains a globally unique identifier (GUID) for the Automation object being created. The GUID will look something like this:

{172BDDF8-CEEA-11D1-8B05-00600806D9B6}

The GUID is the way that the operating system tracks and uses COM objects. The ProgID is simply an alias that is easier for script writers to remember.

After the GUID is discovered, the HKEY_LOCAL_MACHINE\Software\Classes\CLSID portion of the registry is searched for a subkey with the same name as the GUID. When the operating system finds this subkey, it examines the contents for additional subkeys that store the information needed to locate the executable file or library file for the object (in the case of the FileSystemObject, C:\Windows\System32\Scrrun.dll). The COM API loads the application or library, creates the object, and then returns an object reference to the calling script.

Server Mode

When an object is created from an executable file, the application is started in a special mode known as Server mode or Embedded mode. This means that although the application is running and fully functional, there is no graphical user interface and nothing is visible on the screen. (You can, however, use Task Manager to verify that the process is running.) Server mode allows you to carry out actions without a user seeing, and possibly interfering with, those actions.

Although server mode is often useful in system administration scripting, sometimes you might want a user interface (for example, if you are displaying data in Internet Explorer). If so, you will need to use the appropriate command for that COM object to make the application appear on screen. For example, the following script creates an instance of Internet Explorer and then uses the Visible command to allow the user to see the application:

Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True

Binding

Binding refers to the way that a script or an application accesses a COM object. When you create an object reference to an Automation object, VBScript must verify that the object exists and that any methods or properties you attempt to access are valid and are called correctly. This process of connecting to and verifying an object and its methods and properties is known as binding.

COM supports two types of binding: early and late. With early binding, an object, its methods, and its properties are checked when the application is compiled. If there are any problems, compilation will fail. Early binding is faster than late binding because the object is verified before the application runs. In addition, early binding provides access to the object's type library, which contains information about the methods and properties of the object. The information in the type library can then be included within the compiled code and thus be available whenever the application needs it.

Because VBScript is not a compiled language, it does not support early binding. Instead, you must use late binding, in which binding does not occur until the script actually runs. With late binding, the script must access the registry to obtain information about the object, its methods, and its properties. Because VBScript does not have access to the object's type library, it must perform a similar lookup any time it accesses the object or attempts to use one of the object's methods or properties. In addition, any incorrect calls to the object will not be found until the script actually runs.

Choosing a Method for Binding to an Automation Object

Binding to an Automation object is actually quite easy; the hardest part involves knowing how to bind to that object (that is, do you use the GetObject method or the CreateObject method?). For the most part, this depends on the object you are binding to; however, some general guidelines for binding to Automation objects are listed in Table 2.20.

Table 2.20    Methods for Binding to Automation Objects   

To Perform This Task .Use This Method .
Bind to WMI or ADSI.VBScript GetObject and the appropriate moniker.

A moniker is an intermediate object that makes it possible to locate, activate, and create a reference to Automation objects. Both WMI and ADSI are accessed using monikers; this allows your script to locate WMI and ADSI objects without having to know the physical location of these objects. Monikers are typically used to bind to COM objects that reside outside the file system.

Set TestObject = GetObject("winmgmts:")
Bind to a new instance of an Automation object.VBScript CreateObject and the appropriate ProgID.

Set TestObject = CreateObject("Word.Application")
Bind to an existing instance of an Automation object.VBscript GetObject and the appropriate ProgID.

Set TestObject = GetObject("Word.Application")
Bind to an Automation object by using an existing file.VBScript GetObject and the appropriate file path.

Set TestObject = GetObject("c:\scripts\test.xls")
Bind to a new instance of an Automation object, with the ability to receive event notifications from that object.Wscript CreateObject, the appropriate ProgID, and an event mapping variable.

Set TestObject = Wscript.CreateObject _
    ("Word.Application", "Word")
Bind to an existing instance of an Automation object, with the ability to receive event notifications from that object.Wscript GetObject, the appropriate ProgID, and an event mapping variable.

Set TestObject = Wscript.GetObject _
    ("Word.Application", "Word")
Bind to a new instance of an Automation object on a remote computer.VBScript CreateObject, the appropriate ProgID, and the name of the remote computer.

Set TestObject = CreateObject _
    ("Word.Application", "atl-dc-01")

Verifying Object References

The IsObject function allows you to verify that you were able to obtain an object reference. If the GetObject or CreateObject call succeeds, IsObject will return True (-1). If the GetObject or CreateObject call fails, IsObject will return False (0). For example, the following code uses CreateObject to try to obtain an object reference (assigned to the variable TestObject) to a nonexistent object. Because the object call fails, TestObject is not assigned an object reference, and IsObject returns 0.

On Error Resume Next
Set TestObject = CreateObject("Fake.Object")
Wscript.Echo IsObject(TestObject)

Unfortunately, VBScript assumes that once an object reference has been established, that reference will remain valid for the lifetime of the script. That is generally not a problem, particularly for ADSI and WMI, which are unlikely to disappear while the script is running.

The same cannot be said for other Automation objects, however. For example, consider the following script, which starts an instance of Microsoft Word, immediately stops that instance, and then uses IsObject to test whether the object reference is still valid:

Set TestObject = CreateObject("Word.Application")
TestObject.Quit
Wscript.Echo IsObject(TestObject)

When the script runs, IsObject reports that TestObject is still an object because TestObject is still an object reference; it just no longer points to a running instance of Microsoft Word.

There are two ways to work around this problem. One approach is to use WMI to verify that the process (in this case, Winword.exe) is still running. Although this method will work, it requires you to repeatedly query the set of running processes on the computer, something that will slow your script. In addition, matters can get complicated if multiple instances of Winword.exe are running because there is no straightforward method for identifying the instance of Winword.exe that you created and that your object reference refers to. To avoid possible problems (such as a script that inadvertently deletes text from the wrong Word document), your script should use the same instance of Winword.exe all the way through.

A better approach is to add eventing to your script. With this approach, your script can receive notification when specified events occur with your Automation object. For example, should Word quit unexpectedly, notice can be sent to your script that the Word object is no longer available. Your script can then take the appropriate action.

For more information about adding eventing to a script, see "Creating Enterprise Scripts" in this book.

Unloading Objects from Memory

In-process servers (that is, Automation objects encapsulated in .dll files) will automatically unload themselves from memory when the calling script completes. This is because these objects run in the same process as the script; when the script process ends and is thus removed from memory, any in-process servers will also be stopped and removed from memory. For example, the following script creates an instance of the FileSystemObject and then displays a message box. As soon as you dismiss the message box, both the script and the FileSystemObject are removed from memory.

Set TestObject = CreateObject("Scripting.FileSystemObject")
Wscript.Echo "Click here to end the script."

This is not true, however, for out-of-process servers, Automation objects that run in a different process than the script itself. For example, the following script creates an instance of Microsoft Word and then displays a message box. When you dismiss the message box, the script process is unloaded from memory.

Set TestObject = CreateObject("Word.Application")
Wscript.Echo "Click here to end the script."

However, the Microsoft Word process (Winword.exe) will continue to run and remain in memory, even though it is not visible on the screen. This is because there is no inherent tie between the script process and the Word process; anything you do to the script process does not affect the Word process and vice versa. You can verify that the process is still running and verify the amount of memory it is still allocated by using Task Manager, as shown in Figure 2.29.

Click to view graphic
Click to view graphic

Figure 2.29   Automation Object Running After a Script Has Completed

With out-of-process servers, you will typically have to use the method built into the object to explicitly unload it from memory. (You will need to check the documentation for the object to determine that method.) Microsoft Word, for example, is unloaded from memory by using the Quit method. The following script creates an instance of Microsoft Word and then immediately unloads that instance using the Quit method.

Set TestObject = CreateObject("Word.Application")
TestObject.Quit

If you run the preceding script and then check the processes running on the computer, you will not see Winword.exe (unless, of course, you had multiple copies of Winword.exe running).

Nothing Keyword

VBscript includes the Nothing keyword, which can be used to disassociate an object reference and an object. After an object variable is set to Nothing, the variable no longer maintains an object reference and thus cannot be used to control the object. For example, the following code creates an instance of Microsoft Word, sets the object variable to TestObject, and then tries to use TestObject to quit Word and unload the object from memory.

Set TestObject = CreateObject("Word.Application")
Set TestObject = Nothing
TestObject.Quit

When this script runs, the error message shown in Figure 2.30 appears. The script fails because TestObject no longer represents a valid reference.

Click to view graphic
Click to view graphic

Figure 2.30   Working with an Invalid Object Reference

Setting an object variable to Nothing releases a small amount of memory but does not unload the object itself from memory. Because of that, there is generally no reason to set an object variable to Nothing; in effect, object variables (and all other variables, for that matter) are set to Nothing when the script completes. For example, in the following script the last line of code is superfluous: It sets the object variable TestVariable to Nothing, but that would occur anyway as soon as the script ended.

Set TestObject = CreateObject("Scripting.FileSystemObject")
Set TestObject = Nothing


Previous   |  Table of Contents   |  Next



Last Updated: December 11, 2002
Top of Page