In programming, procedures are code structures that contain reusable code. Instead of recreating the series of statements over and over again, you can package your code in a procedure and call that procedure whenever you want that code group to execute. In short, procedures can be used as a way to simplify your code.
Using procedures in your code is a programming technique known as modularizing. Modularized code is easier to read and often easier to work with. Modularizing your code can also aid in debugging by reducing the number of possible error points.
So what types of statements can be modularized into procedures? Well, simply put, any of them. However, procedures should be used to group similar or related statements for a specific purpose or task. Then you can call that procedure anytime that you need to perform the given task without having to recreate each of the steps required.
Take for instance a script that checks your email box for new emails. You first need to connect to the mail server. Then, you need to determine whether or not there are new mails. Finally, you need to return a value that indicates whether or not new mail exists.
Rather than recreating these steps, you could package them in a procedure called CheckNewMail. Then, you could simply call the CheckNewMail procedure any time you wanted to know if new mail existed.
Learning to properly implement procedures is a necessary step as you progress as a programmer. Understanding these procedures is also necessary when using external objects or classes. Now that you have a basic understanding of what procedures are, let’s take a look at the different procedures offered by VBScript and create a few working examples.
VBScript provides two different procedural statements: Sub and Function. While both are basically the same, they do have one major difference. Functions can return a value upon execution while Subs can not.
Subs and Functions are often collectively, and interchangeably, referred to as “methods.”
Both Sub and Function statements are closed with an End statement. Either one may also have parameters, as you’ll see later in this article. Sub and Function blocks are also often grouped at the end of a script for convenience purposes. For now, let’s look at how to construct a Sub block.
Sub PrintMessage
WScript.Echo "Hello, World!"
End Sub
This simple PrintMessage subroutine performs a single task. It displays a message box with “Hello, World!” No matter how simple or complex, a subroutine is just a group of statements. In this case, any time I wanted to display the message box, I could simply call my subroutine.
PrintMessage
Sub PrintMessage
WScript.Echo "Hello, World!"
End Sub
I can call my subroutine by simply using its name. VBScript recognizes this as the name of a subroutine and processes the statements in that subroutine before continuing.
Call PrintMessage
Sub PrintMessage
WScript.Echo "Hello, World!"
End Sub
More appropriately, you can call a subroutine using the Call statement. In more complex scripts, this can add a level of readability to your script by making it easier to recognize a subroutine call. It is also used to discard the return value from any Function, as you’ll see later.
PrintMessage
Function PrintMessage
WScript.Echo("Hello, World!")
End Function
As I stated before, Subs and Functions are essentially the same thing. Here is my example again using a Function instead.
So what’s all this talk about returning values? Often, you may have a set of statements that retrieve or calculate some type of information that you would like to use in your script. You can use a function to get that information and return it. You can then capture that return value in a variable so that it can be used later in your script.
intBorn = 1979
strAge = CalculateAge
WScript.Echo "Mary was born in", intBorn
WScript.Echo "She is", strAge, "years old."
Function CalculateAge
intAge = Year(Date) - intBorn
CalculateAge = intAge
End Function
In this example, I’ve created a very simple function that calculates an age by subtracting a given year from today’s date. My function then returns that number. Notice the last line of my function. I’ve set the name of my function equal to the value that I would like to be returned.
The return value of a function is always of type Variant.
Now let’s take this example and see how we can further control it by using parameters.
Parameters are used to put data into a procedure. Both Subs and Functions may use parameters in VBScript. To understand this concept, however, you first need to understand variable scope.
In VBScript, a variable’s life is determined by its scope at the time it is declared or initialized. In other words, that variable reference is only valid within a certain scope.
There are two scope levels in VBScript, script-level (sometimes referred to as “global”) and procedural. In other words, a variable that is declared within a procedure is only valid within that procedure. A script-level, or global, variable however is valid anywhere within the script. To understand why this is important, let’s take a look at an example.
strGlobal = "This variable is global."
Sub MySub
intProcedural = 5
End Sub
Function MyFunction
Set objFso = CreateObject("Scripting.FileSystemObject")
End Function
In this example, the strGlobal variable is declared at the top-most level, or script-level. This variable can be used anywhere throughout the script and will continue to be valid until it is either destroyed or script execution ends.
The intProcedural and objFso variables are a little different. Since they were created inside of procedures, they may only be used within those procedures. Any attempt to use them outside will result in an error. This is where parameters come into play.
PrintMessage
Sub PrintMessage
intBorn = 1979
WScript.Echo "Mary was born in", intBorn
WScript.Echo "She is", CalculateAge, "years old."
End Sub
Function CalculateAge
intAge = Year(Date) - intBorn
CalculateAge = intAge
End Function
Here, I’ve taken our previous example and moved all of the Echo statements into a subroutine. Now when I run this script is says that Mary is 2008 years old! That’s not right. What’s happening here?
The problem is with the variable intBorn. This variable only exists within the scope of the PrintMessage subroutine. Therefore, it’s not available when the CalculateAge subroutine attempts to perform the age calculation so the year 2008 minus Nothing equals 2008! To avoid this incorrect calculation, we need a way to put the intBorn variable into the scope of the CalculateAge function.
In order to make the intBorn variable available to the CalculateAge function, I have added a parameter. Notice how I’ve changed the Function declaration. When declaring a function or subroutine, you may include a list of comma-separated parameters in parentheses. You must then provide a value for these parameters whenever you call the function or subroutine, as you can see in the Echo statement above.
In the last example, I passed the intBorn variable as a parameter which made it available in a new scope. This is known as passing a variable “by reference.”
When a script runs, the script host stores variables in system memory. It creates a “reference” to the memory address where it stores the “value.” The variable name that we assign is actually a pointer to that reference.
When we pass that variable as a parameter, it is passed by reference, which means that the new scope will use the same memory reference. In other words, if we were to change the variable's value in the new scope, those changes would also appear in the original scope as well.
PrintMessage
Sub PrintMessage
strMessage = " This is my message. "
WScript.Echo strMessage
Call CleanString(strMessage)
WScript.Echo strMessage
End Sub
Sub CleanString(strMessage)
strMessage = Trim(strMessage)
End Sub
Take for instance this example. It simply takes a string and removes any leading and trailing white spaces, and then displays the resulting string. You can see when you run this script that the value of the strMessage variable is changed by the CleanString subroutine. This is because the variable was passed “by reference.”
To better understand this concept, let’s take a look at its converse. The opposite of passing a variable by reference is passing a variable “by value.” When passing a variable by value, the value is maintained while a new reference is created in memory. This is used to preserve the original value of a variable.
PrintMessage
Sub PrintMessage
strMessage = " This is my message. "
WScript.Echo strMessage
Call CleanString(strMessage)
WScript.Echo strMessage
End Sub
Sub CleanString(ByVal strMessage)
strMessage = Trim(strMessage)
WScript.Echo strMessage
End Sub
Look again at my Sub statement. Notice that I’ve included the ByVal keyword to instruct VBScript to pass strMessage by value instead. This preserves the original value of strMessage in the PrintMessage scope by creating a duplicate reference in memory. One is used in the PrintMessage scope and the other is used within the CleanString scope. I’ve added some extra Echo statements so that you can see the value of the variables within each of the different scopes.
There is also a ByRef keyword to specify passing a variable by reference. However, it is unnecessary in VBScript since this is the default behavior.
What about VBScript’s naming conventions? You’ve been told that you cannot have two different variables with the same name, right? In VBScript, two variables may have the same name as long as they are created and used in the context of different scopes.
The second (and probably more common) use of parameters is to provide flexibility for your script by performing a set of predefined actions on different sets of data.
Call PrintMessage("This is the first message.")
Call PrintMessage("This is the second message.")
Sub PrintMessage(strMessage)
WScript.Echo strMessage
End Sub
In this example, I’m using parameters to provide different data to my subroutine each time it runs.
The last concept that you need to be aware of is the proper use of parentheses when calling Subs and Functions. The rules are pretty simple: if you are calling a Sub, you do not enclose parameters in parentheses. If you are calling a Function, you do not enclose parameters in parentheses unless you are capturing its return value.
PrintMessageSub "This uses my Sub."
PrintMessageFunction "This uses my Function."
return = PrintMessageFunction("This also uses my function.")
Sub PrintMessageSub(strMessage)
WScript.Echo strMessage
End Sub
Function PrintMessageFunction(strMessage)
WScript.Echo strMessage
PrintMessageFunction = True
End Function
There is one exception to these rules. You must always use parenthesis any time you are using the Call statement.
Call PrintMessageSub("This uses my Sub.")
Call PrintMessageFunction("This uses my Function.")
return = PrintMessageFunction("This also uses my function.")
Sub PrintMessageSub(strMessage)
WScript.Echo strMessage
End Sub
Function PrintMessageFunction(strMessage)
WScript.Echo strMessage
PrintMessageFunction = True
End Function
The Call statement discards any return value; therefore, you cannot use the Call statement any time that you wish to capture a return value.
Learning to make proper use of procedures (along with parameters and scope) will take your scripting abilities to the next level. It is also important to keep these concepts in mind because they can have a dramatic effect on your script performance as well as the system resources required to run your script. The best way to learn about procedures is to look at code written by other, more advanced programmers. Pay attention to how and why they’ve used procedures.
As you progress, I do have a tip for you concerning procedures:
VBScript is executed linearly (meaning line-by-line) so avoid using procedures excessively. This will only make your code harder to follow. You should only use procedures when absolutely needed or when they can be used to simplify your existing code. Procedures should only be used in moderation.
So sit down and write some code, or go back through some code you’ve already written and look for ways that you can implement procedures to simplify your code. Until next time, keep coding!