Skip navigation.

Lee Harvey's Target Range

1 Click Away

Try..Catch..Finally in ... VBScript? Sure!

, , , , ,

I've been working a lot with VBScript and Windows Scripting Host (WSH) lately. Unfortunately, as many of you VBScript writers know, the error-handling capabilities of VBScript leave a lot to be desired. Luckily, a simple design coding pattern from modern programming languages has revealed a rather unique technique in VBScript that resembles...well, a Try..Catch..Finally block! Yep, you heard correctly. With a liberal dose of multi-line colons ( : ), your VBScript code can attain near perfect Try..Catch..Finally functionality. Note: Guaranteed error-handling, and guaranteed finally block execution in a pseudo-subroutine fashion -- all without requiring an "On Error" statement!

Too good to be true? Here's the catch (no pun intended): You leverage the object lifespan provided by VBScript objects. VBScript supports OOP? Yes sir! Grab a copy of your favorite VBScript documentation, and do a search for "Class".

Basically, here's some pseudo-code that illustrates what we all wish VBScript supported...(yeah, I know VB.NET supports this, bah!)

Sub Func1
   Try
      DoLog "Starting"
      Dim i : i = 65535 ^ 65535 
      MsgBox "Should not see this"
   Catch e
      Select Case e.Number
         Case 6 DoLog "Overflow handled!"
         Case Else DoLog "Unhandled error " & e.Number & " occurred."
      End Select
   Finally
      DoLog "Exiting"
   End Try
End Sub
Call Func1

And here's a reality check...

Class CFunc1
   Private Sub Class_Initialize
      DoLog "Starting"
      Dim i : i = 65535 ^ 65535 
      MsgBox "Should not see this"
   End Sub : Private Sub CatchErr : If Err.Number = 0 Then Exit Sub
      Select Case Err.Number
         Case 6 DoLog "Overflow handled!" 
         Case Else DoLog "Unhandled error " & Err.Number & " occurred."
      End Select
   Err.Clear : End Sub : Private Sub Class_Terminate : CatchErr
      DoLog "Exiting" 
   End Sub 
End Class
Dim Func1 : Set Func1 = New CFunc1 : Set Func1 = Nothing

See a resemblance between the two examples? Sure. And guess what? While the first example doesn't work in VBScript, the second example runs just fine in VBScript 5.6 -- once the DoLog subrountine is defined :smile:

So explain, how does it work? Simple, really. You are defining a Class rather than a Sub-routine. Instantiated Classes (called objects) have a predefined lifespan: a constructor is guaranteed to run once the object is created; and a destructor is guaranteed to run once the object is destroyed. You simply leverage these innate object abilities to handle errors gracefully -- all without using "On Error" statements!

I hope you find this exercise fun, if not useful. I've been rewriting some larger WSH VBScripts using this new technique, and so far so good. I've thrown some various crash scenerios (from the past) at them, and not only did they manage to gracefully handle the errors, but the scripts completely finished without dying mid-way through them -- like they used to.

Enjoy.

Proxy Automatic Config (PAC) File TipsGrisoft AVG Free and encrypted All Users\Application Data folder = bad

Comments

avatar
Very neat. I used the idea of the Class_Terminate being always called before the page goes out of scope for an ASP page. This way, I can always catch the errors.

Can you give me an example of how you would use this in your WSH script. I understand how it works but I was wondering how you would embed it in your script. I'm also looking to use it in ASP. Most of my development is in Classic ASP. We haven't been Dotted yet.

Thanks,

Len

By LenChaney, # 23. August 2007, 13:57:05

avatar
Here's a quick example that retrieves Symantec's ThreatCon Level, and outputs it to the console.


Option Explicit

Dim exitcode : exitcode = 0

Sub HandleError(ByVal c)
exitcode = exitcode Or Err.Number
If Err.Number = 0 Then Exit Sub
Select Case Err.Number
Case Else
WScript.StdErr.WriteLine "Unhandled " & c & " error " & Err.Number & ": " & Err.Description
End Select
Err.Clear
End Sub

Class CWeb
Private xml

Private Sub Class_Initialize()
Set xml = CreateObject("Msxml2.XMLHTTP")
End Sub

Private Sub Class_Terminate()
Set xml = Nothing
HandleError "CWeb"
End Sub

Public Function GetPage(ByVal url)
xml.Open "GET", url, False
xml.Send
GetPage = xml.responseText
End Function
End Class

Class CThreatCon
Private regEx
Private match
Private matches

Public level
Public msg

Private Sub Class_Initialize()
Set regEx = New RegExp
End Sub

Private Sub Class_Terminate()
Set regEx = Nothing
Set matches = Nothing
HandleError "CThreatCon"
End Sub

Public Sub Parse(ByVal s)
regEx.IgnoreCase = False
regEx.Global = False
regEx.Pattern = "<img src=""/img/threatcon/threatcon_level([0-9])\.gif"" border=0></p>\W+<p>([^<]+)<"
Set matches = regEx.Execute(s)
For Each match In matches
level = match.SubMatches(0)
msg = match.SubMatches(1)
Next
End Sub
End Class

Class CAllSystems
Private web
Private threatCon

Private Sub Class_Initialize()
Err.Clear
Set web = New CWeb
Set threatCon = New CThreatCon
End Sub

Private Sub Class_Terminate()
Set web = Nothing
Set threatCon = Nothing
HandleError "CAllSystems"
End Sub

Public Sub Go()
threatCon.Parse web.GetPage("http://www.symantec.com/avcenter/threatcon/")
WScript.StdOut.WriteLine "Level=" & threatCon.level & ". " & threatCon.msg
End Sub
End Class

Dim AllSystems : Set AllSystems = New CAllSystems
AllSystems.Go
Set AllSystems = Nothing

WScript.Quit exitcode


Note: In this case, I used a global error-handling subroutine for the classes.

By Lee_Harvey, # 23. August 2007, 19:37:37

Write a comment

You must be logged in to write a comment. If you're not a registered member, please sign up.