Thursday, May 24, 2007

Script With Class in VBScript

Someone asked me to help with a script and to also give some assistance in how best to use VBScript to accomplish tasks simply and quickly.  I took the opportunity to inject a "Class" that I use frequently to send formatted output to a log file.  This seems to have caused no end of confusion for the poor soul whose burden I was trying to lighten.

The use of classes in VBScript appears to be not well understood.  Consider the following:  You use an object to retrieve WMI information.

Set objWMI = GetObject("winmgmts:...)

The "objWMI" object is an instance of a "Class".  You access it's properties and methods using the "dot" notation: objWMI.Name.

A VBScript Class looks like the following:


1 Class CMyClass 2 3 Sub Class_Initialize 4 End Sub 5 6 Sub Class_Terminate 7 End Sub 8 9 End Class

The "Subs" re called on the creation of an instance and when an instance is destroyed.

Set objMyClass = new CMyclass

The class methods are called like any other object.  If CMyClass had a "Write" method we would call it like this: objMyClass.Write "Hello World!"

Why is this any different or better than using a plain old "Sub".  Bear with me a bit and I will show you some neat stuff and then maybe, some magic.


   1:  Class CLogFile
   3:      Dim fso 'FSO object
   4:      Dim ofile ' FIle object
   5:      Dim sFileName ' saves the pre built filename
   6:      Dim bLocal ' ' Setinstance to use WScript.Echo instaed of file
   8:      Sub Class_Initialize() ' this happens for every instance
  10:          sFileName = sLogPath & "\" _        
  11:              & Right("0" & Month(Date),2) & "-" _        
  12:              & Right("0" & Day(Date),2) & "-"  _        
  13:              & Right(Year(Date),2) & "-"  _        
  14:              & DatePart("h", Now) & DatePart("n", Now) & ".txt"
  15:              Set fso = CreateObject("Scripting.FileSystemObject")        
  17:      End Sub
  19:      Sub Open( sLogPath ) 'Call at begiing of script     
  21:          if sLogPath = "" Then             
  22:              bLocal = True      ' Echo don't use file       
  23:          Else  'open/create file     
  24:              Set ofile = fso.OpenTextFile(sFileName , 8,True)        
  25:              Write "Log file being opened"             
  26:          End If
  29:      End Sub
  31:      Sub Write( sMessage )  
  33:          If Not bLocal Then              
  34:              ofile.WriteLine "[" & Now & "] " & sMessage          
  35:          Else              
  36:              WScript.Echo "[" & Now & "] " & sMessage          
  37:          End If    
  39:      End Sub        
  41:      Sub Close  ' call at end of script to timestamp the log closing.
  42:          Write "Log file being closed"
  43:          ofile.Close
  44:          ' clean up for good practice
  45:          Set ofile = Nothing
  46:      End Sub  
  48:  End Class

This is the bombshell I dropped on my friend.  It looks foreboding but in a bit you will see what it buys us.

1.  Encapsulation:

All variables required for the functionality of the included code are embedded in the class.  This makes it convenient to copy and paste as it doesn't need external feeding.  No more global FileSystemObject.  It's hidden inside the class

2.  Abstraction:

The functionality is totally hidden.  It just does what we want without having to think about how it's done.

3.  Portability

4.  Reusable Code:

Code can be used over and over to perform the same function without the need for redesign.

In the case of this LogFIle class the output format can be changed easily without changing any of the calling code.  This code uses a specific format for the file name which it generates.  This can be easily altered.  A method can easily be added to handle switching between formats for different uses.  The log lines contain a time stamp which can be easily turned on or off.

The actually version of this that I use is somewhat more complex as the "Write" method takes an array of values and the "Open" method takes a formatter spec.  The class can switch between text, CSV, HTML and XML outputs.  This allows the same code to be used over and over even in the same script.  Each "Set obj = new Class" can open a different output file with a different format.  This object can even output to the command line.  The Class I use outputs to either the command line StdOut or the StdErr or both.  I have a "Write" "WriteEx" and a "WriteError" method that are used to control where the output goes and how it is both tagged and formatted.

 Study the above code and you will see how it accomplishes numerous effects with very few lines of code. 

 Later I might publish my more sophisticated Log class.