Advanced VBScript Topics
I want to cover some important topics here for more advanced programmers, to help you get going in the right direction. Don't worry if these topics don't make sense now; they're applicable only to the most complex VBScript programs.
Procedures: Functions and Subroutines
Procedures are the building blocks of large VBScript programs. A procedure is a separate section of code in your program that performs a particular task. The two types of procedures are functions and subroutines.
You have already learned about many of the built-in functions in VBScript, but you can create your own, too. You can also create subroutines, which are like functions but don't return a value.
The great power in procedures is that after you have written them once in your code, you can call them as often as you want. You can concentrate on how to perform a particular task just once when you write a function so that later you can simply use the function without worrying about the details.
To create a function, use the Function...End Function statements. Put any functions and subroutines after the main body of your script. (While VBScript will run any statements in between or after your functions and subroutines as part of the "main program," scattering the code around the file this way can be extrememly confusing for anyone reading your script.)
As an example, the following simple function accepts one argument and returns the value of the argument plus 2:
function addtwo (value) addtwo = value+2 end function
The (value) part tells VBScript that the function expects one argument and is to hold the value in a variable named value. This variable is local to the function: if your script also uses a variable named value, it is not altered when function addtwo is called. Instead, addtwo has its own, temporary variable named value that exists only while addtwo is working.
The following is an example of how this function might be used elsewhere in the script:
a = addtwo(3)
When VBScript encounters the expression addtwo(3), it begins to run through the statements of the function, and the variable value holds the argument 3. The value to be returned by the function is set when the program assigns a value to addtwo (that is, to the variable with the name of the function itself). As you might guess, the end result is that ultimately the script assigns the value 5 to variable a.
In this way, you can extend the built-in functions of VBScript with your own. When you choose names for the argument variables, such as value in the example, you should choose meaningful names that explain what type of information is expected. For example, when a function is to be given a filename, I often name the argument fname, just out of habit. In my scripts, a variable or argument named fname always holds a filename.
The following function takes a filename, removes its extension (its file type), and adds the extension .OLD. You might use this function in a program that is going to update a file. If you want to create a backup copy of the file before changing the original, you can use this function to determine the desired name for the backup file, given the original file's name:
' example file script0215.vbs function backup_file_name (fname) idot = instr(fname, ".") if idot > 0 then backup_file_name = left(fname, idot-1)+".OLD" else backup_file_name = fname+".OLD" end if end function
Here's how the function works: The Instr function searches its first argument (fname, in this case) for the first occurrence of the second argument (in this case, just a period character). It returns the value 0 if the second string is not found; otherwise, it returns the position within the first string at which it was found—1, 2, and so on. So, here, idot is set to 0 if no period appears in the filename argument fname, or it is set to a number greater than zero if a period does appear.
The remainder of the function computes the backup filename. If the period is found, the function constructs the new filename from the first characters of the old name, up to but not including the period, and adds .OLD. If no period is found in the old name, the function tacks on .OLD anyway. Put into use, the following statements would set variable file1 to MY.OLD, and file2 to SOMEFILE.OLD:
file1 = backup_file_name("MY.DATA") file2 = backup_file_name("SOMEFILE")
Subroutines are like functions, except they don't return values. They're used to do some specific job that the remainder of your script can then take advantage of. For example, the following subroutine takes as its argument the name of a file. It prompts the user to specify whether the file can be deleted; if the user clicks OK, it deletes the file:
sub maybe_delete (fname) if msgbox("Should I delete file " & fname & "?", vbYesNo) = vbYes then fso = CreateObject("Scripting.FileSystemObject") fso.DeleteFile(fname) end if end sub
This subroutine uses the built-in function MsgBox to ask the script's user whether the file can be deleted. It displays a dialog box with two buttons: Yes and No. If the user clicks Yes, the program uses a FileSystemObject to actually delete the file (I'll cover FileSystemObject in Chapter 4.)
With this subroutine at the end of your script file, you could use statements like this in your program:
maybe_delete filename maybe_delete "OLD.DATA"
You can see how this lets you concentrate on the task at hand (deleting certain files) as you write the script, knowing that the details of how to delete files are taken care of in the subroutine.
VBScript supports array variables. Arrays are variables that hold more than one distinct value. An array is a bit like an apartment building, where each apartment has a separate occupant. Just as the units in an apartment building are numbered, the individual values in an array are identified by a number called an index. Arrays are used when a script has to manage a varying number of items (say, usernames or filenames).
Whereas you can create an ordinary variable in VBScript simply by assigning a value to it, you must tell VBScript that a variable is to be an array before you first use it. Arrays are declared using a dim (or dimension) statement, which tells VBScript the largest index value you intend to use. Array indexes start at zero, so the statement
actually creates an 11-element array, with values var(0), var(1), ... and var(10). Once it's declared, you can assign values to the elements of the array like this:
var(0) = "First value" var(1) = "Second value" var(2) = "Third value"
The big advantage of using arrays is that they let you write programs that are independent of the number of items you need to process. Using the looping statements that we discussed earlier in the chapter, you can run through the items in an array and process each one in turn. Here's an example:
' set up an array with three items dim var(3) var(0) = "First value" var(1) = "Second value" var(2) = "Third value" nitems = 3 ' run through all items in the array using For Each for each value in var wscript.echo value next ' run through the items using indexing for i = 0 to nitems-1 wscript.echo var(i) loop
In this example, I first created an array with three elements and stored information into each one. It's not necessary to use all the elements; you can make arrays larger than you need if you don't know in advance how many items you'll put into them.
The example prints out the contents of the array twice. The first time, it uses the For Each loop, which assigns the values in the array to a variable, each in turn. The For Each loop is the easiest to write, but it's not useful unless you want to process every one of the array's elements.
The second loop runs through the array by indexing each value in turn. This loop is a bit more complex to write, because array indexes start with 0 and you have to take this into account when deciding where to stop. To visit every element, the indexes must run from 0 to the number of items minus 1. In this example, there are three items, so we need the For loop to run through values 0, 1 and 2. Yes, this is awkward, but it's just the way arrays work in languages such as C and VBScript, and it's the basis of a common pattern.
This is a trivial example, but it shows how arrays can make short work of dealing with a large number of items. In this example, I'm only printing the array values out, but the script could do much more. When you use arrays, you only need to write instructions to handle the general case for one item (for example, var(i)), and the loop takes care of repeating the work with every item you need to process.
If necessary, arrays can be set up so that their size can be changed as the program runs. The array must first be declared without any dimensions, like this:
Then, before its first use and anytime thereafter, the array's dimensions can be set or reset with the ReDim statement, like this:
ReDim [preserve] varname(subscripts) [, ...]
The preserve keyword causes VBScript to preserve the existing data in arrays being resized; without preserve, the variables are cleared, and all elements are set to Nothing. (Of course, even with the preserve keyword, if you make an array shorter than it was before, you will lose any information stored in the elements that are dropped from the end.)
By default, when a variable is declared or created in the main body of a script file, it has global scope; that is, it's also accessible to every procedure called in the script. Variables defined within functions or subroutines, however, have private scope by default. They're accessible only within the procedure in which they're declared. If a procedure containing a variable called var calls another procedure, that procedure can't access this variable. This protects variables in your procedures from being inadvertently altered by other procedures. When a procedure that defines the variable terminates, the variable is destroyed.
If you want to explicitly create variables with a global scope, you can do so by using the public statement. The same goes for private variables by using the private statement. Public variables are accessible to any procedure and are persistent until the script ends. For example, a procedure can use the following statement to declare a variable, Fsh, and an array, MyArray, that are available to all procedures in the script:
public Fsh, MyArray(10)
By default, variables can be used without being declared (or dimensioned) in advance. Seasoned programmers know that this can lead to hard-to-find bugs, because it's difficult to detect typos; you could simply create a new misspelled variable where you had intended to change the value of a preexisting one. The Option Explicit statement fixes that by requiring you to declare all variables before using them, using a dim, public, or private statement. I recommend this in complex scripts. It takes a little extra work when you first write the program, but it helps eliminate a whole category of bugs.
To take advantage of this feature, put the statement option explicit as the first line of your script file. Then, follow this with dim statements listing each variable you'll use in the script. Yes, Dim is used to declare array variables, but it can also be used to define normal variables as well.
Here's an example of a script with a typo:
option explicit dim myname myname = "Brian" wscript.echo "Hello", mynam
This script will generate an error message when it runs into the undefined, misspelled variable mynam. Without option explicit, this script will simply print "Hello" and there will be no other indication of the mistake.