A Matter of Class
Taking an object-oriented approach to scripting can help your variable structure run like a well-oiled machine.
- By Chris Brooke
- June 01, 2001
My wife just delivered our new son—Samuel John
Brooke! And he’s cute as a button, too. As promised,
the digital video camera was set up (at a discreet
angle, of course) and I recorded everything. As
I watched the hospital staff work, I was impressed
by the object-oriented approach they took. One
nurse cleaned Sammy while another got the bassinet
ready and still another helped the doctor with
my wife. Each person had his or her own set of
tasks to accomplish and only interacted with each
other in limited ways. Hmm… sounds very
familiar.
In previous columns, I’ve embraced a similar
approach to scripting by using Subs and Functions
to segment code into discreet procedures, each
with limited—but important—functionality. I’ve
even extended this paradigm through the use of
built-in and third-party components. Over the
next couple of months, I’m going to look at another
way to encapsulate functionality into scripts:
VBScript classes. This topic was suggested by
Gordon Comrie, who is the winner of a copy of
Macmillan Publishing’s Windows NT/2000 ADSI
Scripting for System Administration by Thomas
Eck.
Homework
Before we jump into classes, let’s review
our homework from last month. The following script
uses the Outlook and Excel type libraries to read
contact information from a spreadsheet and add
each person to Outlook as a contact.
' Excel2Outlook.vbs
' OutlookExcel.vbs
Option Explicit
Dim objOutlook, objContact, objExcel, iRow
' Start
Outlook
Set objOutlook = CreateObject("Outlook.Application")
' Open
Spreadsheet
Set objExcel =CreateObject("EXCEL.Application")
objExcel.Visible = False
objExcel.Workbooks.Open "C:\Contacts.xls"
objExcel.Sheets("New").Activate
objExcel.ActiveSheet.Range("A1").Activate
iRow=0
Do While objExcel.ActiveCell.Offset(iRow,0).Value<>""
'
Setup Contact information...
' Create and Open a new contact.
Set objContact = objOutlook.CreateItem(2)
' 2=Outlook
Contact Item
With objContact
.FullName = objExcel.ActiveCell.OffSet(iRow,0).Value
& " " & objExcel.ActiveCell.Offset(iRow,1).Value
.Email1Address = objExcel.ActiveCell.Offset(iRow,2).Value
.CompanyName = objExcel.ActiveCell.Offset(iRow,3).Value
End With
'
Save Contact...
objContact.Save
Set objContact = Nothing
iRow = iRow+1
Loop
objExcel.Application.Quit
Set objExcel = Nothing
Set objOutlook = Nothing
WScript.Quit
Rather than use procedures (as I alluded to last
month), I decided to put the main logic of this
script into a simple Do...Loop. I also chose to
reference each cell in the spreadsheet via its
offset from the active cell, rather than constantly
switching the active cell. As you can see, this
script requires that the spreadsheet be in the
expected format, on a worksheet named “New” and
with the specified filename. It’s a simple matter
(and it’s been done many times before) to pass
the filename via the command line and perform
some basic error checking.
One final note before we move on to classes:
You’ll notice that I put the While clause at the
top of the Do...Loop section. Although in a Do...Loop,
you can either Do While...Loop or Do...Loop While.
I put it at the top in case the spreadsheet was
empty. If there’s nothing in the first cell, the
script skips this entire section and exits. Now,
on to classes!
OOPs!
It’s a bit ironic that the acronym for
object-oriented programming is “OOP.” Indeed,
by using classes to encapsulate functionality,
I’m able to circumvent quite a few scripting “oops”
moments. Classes are much more object-oriented
than procedures (Subs and Functions). In fact,
they’re the building blocks of advanced components.
Let’s start by looking at a very simple class.
' SimpleClass.vbs
Class Customer
Public FName
Public LName
Public EMail
Public Company
End Class
This Customer class is used to hold data. As
such, it contains only properties. Each property
(aka: variable) in this class is declared using
the Public keyword. This is equivalent to the
Dim statement. It tells VBScript that these properties
aren’t local and can be accessed from outside
the class. If I want to declare some variables
that’ll just be used within the class, I declare
them with Private. I can now use this class to
store data by assigning it to a variable:
Dim clsCustomer
Set clsCustomer = New Customer
clsCustomer.Fname = "Joe"
clsCustomer.Lname = "Smith"
clsCustomer.Email = [email protected]
clsCustomer.Company = "The Geek Shop"
Or I can set up an array (remember those?) to
hold multiple customers:
Dim clsCustomer(20)
Set clsCustomer = New Customer
clsCustomer(1).Fname = "Joe"
clsCustomer(2).Fname = "Sam"
…etc.
Not all that impressive, is it? Well, stay with
me here, because it gets better. First, a few
standard operating procedures for using classes.
Although your class declarations can be put anywhere
in your script, it’s customary to put them at
the end, along with any Subs and Functions you
may have. This makes it easier to cut and paste
them for re-use.
As indicated in the previous item, classes don’t
replace procedures. You’re sure to have many scripts
that utilize both classes and procedures to accomplish
a given set of tasks.
Classes are instantiated (created) in much the
same way as components (i.e. using the Set command).
The only difference is that they’re created using
the New keyword rather than CreateObject. In Visual
Basic, you can use the New keyword to instantiate
both classes and components. In VBScript, you
only use New when dealing with classes. The reason
for this is a bit complicated and has to do with
how VBScript binds to components (late binding)
as opposed to Visual Basic (early binding).
The Good Stuff
In addition to simply containing structures
of variables, classes can also encapsulate procedures.
For example, let’s say you’ve created several
Subs and Functions you seem to use all the time.
Rather than copy and paste each of them into every
script you write, you can create a class that
contains all of them. This class can then be added
to every script and accessed like any other component.
Next month, I’ll create some advanced classes
that contain both properties and methods (procedures).
I’ll also discuss some of the more advanced concepts
that are inherent to classes. Now, if you’ll excuse
me, Sammy’s telling me that he wants his 2 a.m.
feeding.