Get Yourself Organized

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.

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.

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:

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!

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 chrisb@componentsource.com.