PreviousNext…

Starting with Lotusscript (Part 3)

Background

As a Notes user, have you used an application that does something weird, then tells you this?

Argh! Object variable not set!

Perhaps you've also received Type mismatch errors when all you were doing was going about your normal business. Well, these errors are often down to poor / non-existent error handling on the part of the coder. Here's how to avoid that kind of nonsense…

On Error Goto…

Lotusscript is a procedural, or linear language. By this I mean that your code kicks off, and runs, bumpity-bumpity-bump to the end. Sure, there can be some branching and stuff like that, but ultimately it's a BASIC-derived language, and therefore linear (object models and the like notwithstanding). This means that you can often see the dreaded Goto statement dotted around (note: the link is a 123KB PDF)

Now, I don't particularly like to use the Goto statement, but sometimes we don't have much choice. Whilst I like to break up reams of impenetrable code into nice logical parcels held within sub-routines and functions, I have to use the Goto statement to cover my arse with regards errors. The line you may be aware of is this:

On Error Goto lblErrs

Forget the "lblErrs" bit for the time being — that's just a custom "label" I use for the actual error handling code — but I'm sure you can see what we're telling the program to do here. As soon as an error is hit, go to the named label. Why do we have labels? Well, some of you may well remember the heady days of early '80s BASIC programming on ZX Spectrums, BBC Micros, and the like. I think just about everyone went into their local electronics store and did something like this to the demo machines:

10   Print "Ben woz 'ere";
20   Goto 10

Oh how we laughed. Well, back then we had BASIC programs with line numbering. In Lotusscript and VB we only use line numbers for referencing, we don't use them within the actual code (apart from maybe in error handlers, when we use the Erl function), so we have labels instead. Time for some sample code to illustrate this I think.

On Error Goto lblErrs

Dim session As New NotesSession
Dim db As NotesDatabase
Dim vw As NotesView
Dim doc As NotesDocument

Set db = session.CurrentDatabase
Set doc = vw.GetFirstDocument

Exit Sub

lblErrs:
Messagebox "An error has occurred. The error was '" & Error$ &_
"' code number, " & Err & ".", 0, "Error!"
Exit Sub

See? You can spot the obvious error I hope. Notice how the code runs. Right from the off we tell Lotusscript to go to the "lblErrs" section of the code if it hits an error. When it does ("Object variable not set", funnily enough), the program flits from what it was doing, to the "lblErrs" section. Once there, it performs the error handling code and then exits. Of course, in real life, if you have an Option Declare or Option Explicit line in there like I've warned about before, then you will catch this particular error before runtime.

Trapping expected errors

Now you don't have to exit, and sometimes may not want to. One Lotusscript statement I use quite a lot is MkDir. This, surprisingly enough, creates a directory on the local machine in the specified place. But if this directory already exists, Lotusscript throws an error. This isn't really an error as far as we're concerned. What we really want to say is: "fine, so the directory already exists, that's OK. Just carry on and do what I want you to do with this directory in my program." But if I didn't have an error handler, the user would see a message from Notes about an error, which isn't very pleasant. In this case, I would argue it's perfectly OK to code an error handler thus:

lblErrs:
If Err = 75 Then
Resume Next
Else
Messagebox "An error has occurred. The error was '" & Error$ & "' code number, " & Err & ".", 0, "Error!"
Exit Sub
End If

"Resume Next" eh?

This does what it says on the tin: the routine may well invoke the error handler, but the handler then tells the code to carry on with the lines falling immediately after the piece of code that triggered the error. You can use other Resume calls (for example, Resume 0), but this is the most commonly used. This can be very useful, but is also where I get annoyed with some code…

… Do not use Resume Next in every single error handler! I've seen this done. "Oh yes, we'll code some error handling" and then Resume Next just gets dumped in there. This is not addressing the problem, it's anything but "handling the error." Your code will not fail gracefully when something unexpected happens, because whilst the error will be trapped, your code will run on regardless.

For example, say a NotesDocument object is supposed to get set, but doesn't. As soon as you reference this object you're going to hit — you've guessed it — "Object variable not set." If you then do a Resume Next call, every subsequent line referencing the non-existent NotesDocument is going to trigger an error too. Urgh. Don't do it, unless there's a good reason. Please.

The key thing to remember is to ensure your code is appropriate. Error-handling routines go hand in hand with general "traps" in your code. What do I mean by this? Well, taking the example above, consider some code like this:

Dim session As New NotesSession
Dim db As NotesDatabase
Dim vw As NotesView
Dim doc As NotesDocument
Dim strValue As String

Set vw = db.GetView("MyView")
Set doc = vw.GetFirstDocument
strValue = doc.Subject(0)

Call processDoc(strValue, vw)

What happens if the NotesView doesn't get set? The NotesDocument object will be null, and therefore any references to it will trigger an error. This has the knock-on effect of rendering strValue empty, which will kill the processDoc()routine, and so on… I would litter my code here with "traps" — little If… Else… statements that test the various variables and objects are empty or null. Again, this is simply good practice.

Conclusion

I hope this has been a useful primer in error handling; just bear in mind two things:

1.   Your handler needs to do something useful.

For example, perhaps you're just going to code a handler to give your users meaningful messages when something happens. Perhaps you want to record errors as log entries in a database (e.g. a scheduled agent using the NotesLog class), or maybe you want to send a page / email / SMS to someone when some code fails. You might even want to do the Lotusscript version of Log4j, courtesy of Johan Känngård!

Other typical runtime errors that can be / should be "handled" include those associated with mailflow. "Send" calls can result in errors (duplicate names in the Domino Directory, no matching names found, and so forth). It's always nice to trap these in your handler, and give the user a meaningful error message (rather than the usual Notes techno-speak), with perhaps a suggested alternative course of action.

2.   Your handler needs to be appropriate. A snippet of code as basic as this…

Dim workspace As New NotesUIWorkspace
Dim uiDoc As NotesUIDocument

Call uiDoc.Print

… arguably does not really need a handler. ;-)

Further reading

Comments

  1. I also like to add the place the error comes from (sub or function name) and the line number as well. The line number can be achieved with the Erl function. As for the "source" of the error, I used to have a generic function in a script library that had a String parameter that would be specified when calling that function. Just my 2 (Canadian) cents B-DBenoit Dubuc#
  2. how can I use lotusScript with domino designer of lotus notes to create a new programm ?ammar#
  3. Well, I'd start off by reading a good book / Domino Designer help. Or maybe you could read this:
    Starting with Lotusscript (Part 1)Ben Poole#
  4. Nice articles Ben, specially for those guys that are starting in notes. Let me suggest to add another useful piece of data when debugging errors: You use the Err (that get the error code), the Error (what is an explanation of what the error is), and I use additionaly Erl, this one indicates the line where the error is coming from. This is very useful when you have dozens of lines of code.

    Best regards .
    ::AleX::.
    Dominocode.Net

    Alex Hernandez#
  5. I briefly mention Erl in the article, and you make a good point — especially useful for loooooooong pieces of code!

    Ben Poole#
  6. You could also use Lsi_Info(2) to get the currently executing method/function/sub, and Lsi_Info(12) to get the method/function/sub that invoked the current method/function/sub. I also use Typename(Me) + "." + Lsi_Info(2) when logging / throwing errors in methods inside classes.Johan Känngård#
  7. If you are inside a function, and want to trap an error then call an errorfunction to write to a log, where should you put your on error statement and your resume next?Alison Hall#

Comments on this post are now closed.

About

I’m a developer / general IT wrangler, specialising in web apps, the mobile web, enterprise Java and the odd Domino system.

Best described as a simpleton, but kindly. Read more…