A Matter of Class

Taking an object-oriented approach to scripting can help your variable structure run like a well-oiled machine.

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.

Featured