Functions not only help you organize your scripts into subroutines, they also let you choose between going global or staying local.

Get Yourself Organized

Functions not only help you organize your scripts into subroutines, they also let you choose between going global or staying local.

One of the most important aspects to scripting is keeping your scripts organized. The best way to do this is to separate the various functions into different Functions (and Subs). Last month we got our first real taste of user-defined procedures when I used Subs to separate the functionality of my ResetPW.vbs script. I hope I didn’t leave you too shell-shocked, since I did kind of spring it on you all of a sudden. This month we’re going to take a closer look at Subs and Functions and see if we can’t clear up a few things.

Subs and Functions

Putting different functionality into different sub-routines is nothing new. It ensures that the logic of your scripts is easy to trace; it also reduces potential errors. The most organized way we have of, well… organizing our scripts is through the use of Subs (short for either subroutine or subprogram, depending on who you ask) and Functions. Subs and Functions are essentially the same with one exception: a Sub does not return a value (at least not directly), a Function does. Let’s use a simple script to illustrate the difference between Subs and Functions.

'Hello World! w/ Sub
Call HelloWorld
Sub HelloWorld
WScript.Echo "Hello World!"
End Sub

'Hello World! w/ Function
Dim strHello
strHello=HelloWorld
   WScript.Echo strHello
Function HelloWorld
   HelloWorld="Hello World!"
End Function

The output of both scripts is simply the display of “Hello World!” Sometimes, however, it’s important to have a value returned to the script. This is where a Function can come into its own.

Conjunction Junction, What’s Your Function?

The ability to return a value to the script can be quite important. In the simplest case, this can be merely a Boolean value (true/false, yes/no). Let’s use a built-in VBScript Function—MsgBox—to demonstrate this:

'MyFunction.vbs
Dim bAnswer 'Yes for OK, No for Cancel
bAnswer=MsgBox("Click to continue",
   vbOKCancel, "Continue?")
If bAnswer=False Then WScript.Quit

Rest of script

The MsgBox Function displays a window with “Click to continue” and two buttons: “OK” and “Cancel.” If the user clicks OK, the function returns a “True” value and the script continues. If the user clicks Cancel or hits Escape, the function returns a “False” value and the script terminates. Of course, you can set up user-defined Functions to return whatever value you wish.

The Tip of the Iceberg

Keeping things organized is only one advantage to using Subs and Functions. One of the most useful features inherent to procedures is that any variables declared within a procedure are local to that procedure only. They can’t be used by the rest of the script. Up to this point, we’ve always placed our variable declarations at the beginning of the script. Variables declared in this way are available to the entire script—procedures and all. There will be times, however, when you’ll want to keep certain variables local to the procedure in which they’re used.

'KeepEmLocal.vbs
Option Explicit
Dim strHello
strHello="Hi!"

WScript.Echo strHello
Hello1
Hello2
WScript.Echo strHello
WScript.Quit

Sub Hello1
   Dim strHello
   strHello="Hello World!"
   WScript.Echo strHello
End Sub

Function Hello2
   Dim strHello
   strHello="Hi Ya'll"
   WScript.Echo strHello
End Function

Figure 1 shows the output from this script. 

Figure 1. Keeping the variable inside the procedures keeps its value local to the subroutine. (Click image to view larger version.)

As you can see, by declaring the variable inside the procedures, we’ve kept its value local to those Subs. The changes I made to strHello inside the Sub Hello1 and the Function Hello2 had no effect on the value of strHello that was declared in the main body of the script. Now, I know what you’re thinking… “strHello was first declared in the main body of the script and should have been available to all the Subs and Functions.” It was and it is. But, since we re-declared strHello inside the procedures, two new variables were created with the same name and are private to those procedures. I’ll prove it. Add another Sub to the script called Hello3.

Sub Hello3
   WScript.Echo strHello
End Sub

Insert a call to Hello3 right after Hello2. Run the script again and the output looks like Figure 2. Hello3 was able to use the original value of strHello (“Hi!”) that was set at the beginning of the script because it wasn’t independently declared inside the sub! If you don’t declare the variable inside the procedure, VBScript assumes that you want to use a “global” variable.

Figure 2. In this script, Hello3 uses the original value of strHello ("Hi!"). (Click image to view larger version.)

This “variable independence” that Subs and Functions possess also extends to other areas. You’ll recall in last month’s column that the On Error Resume Next statement was also able to be used independently within procedures, as well as globally.

This Is All Neat and Everything, But…

Let’s take a look at how we can apply this knowledge in some practical way to our scripts:

'KeepEmLocal.vbs

Option Explicit
Dim objFSO, objFile

Set objFSO=CreateObject
   ("Scripting.FileSystemObject")
Set objFile=objFSO.GetFile
   ("c:\MyFirstFile.txt")
WScript.Echo objFile.Name

MySub

WScript.Echo objFile.Name

WScript.Quit

Sub MySub
   Dim objFSO, objFile
   Set objFSO=CreateObject
      ("Scripting.FileSystemObject")
   Set objFile=objFSO.GetFile
      ("c:\MySecondFile.txt")
   WScript.Echo objFile.Name
   MySub2
   Set objFSO=Nothing
   Set objFile=Nothing
End Sub

Sub MySub2
   WScript.Echo objFile.Name
End Sub

As you can see from the script and in Figure 3, keeping variables private also extends to objects. In the above script we create the same object twice—once globally, once within a Sub. In line 8 we echoed the first file name to the screen. We then created a second object (with the same name) in MySub. We echoed its name to the screen, then called MySub2 to do it again. However, MySub2 echoed back the MyFirstFile.txt instead of MySecondFile.txt because it was declared globally!

Figure 3. The concept of keeping variables private extends to objects. (Click image to view larger version.)

I’m way ahead of you. You’re about to ask “Why not just create another instance of the FileSystemObject with a different variable name, like objFSO2?” The answer lies in the root of what makes procedures valuable in the first place: Reusability! Now that we’ve established the fundamentals of Subs and Functions and how they keep variables private, I want you to write a script to use the same procedure (using only one object!) to compare the “DateLastModified” property (hint: use the FileSystemObject) of two files. I’ll have my version for you next month, as well.

About the Author

Chris Brooke, MCSE, is a contributing editor for Redmond magazine and director of enterprise technology for ComponentSource. He specializes in development, integration services and network/Internet administration. Send questions or your favorite scripts to [email protected].

comments powered by Disqus
Most   Popular