What Can You Do?

We’re talking about errors here, not bugs. Errors are bad things that can happen. Bugs are bad things that should never happen. Use asserts to avoid bugs. It’s not a bug if the user misspells a filename in a dialog box, but you do have to do something about it. Here are some of the error-handling strategies that Visual Basic programmers adopt:

Let ’er rip. Visual Basic has built-in error handling, so you don’t have to do anything. If the user does something wrong, a nice, friendly error box will pop up with a clear, intuitive message, and your program will terminate. If your users complain, blame Visual Basic. If you think this is a good idea for clients, you deserve your horrible fate. But sometimes this terrible-sounding technique is actually the right strategy for components.

Forbid errors. Don’t laugh. This is sometimes a viable option. If you consider all input to be valid, don’t do anything that could fail and make sure all your output is valid. Then nothing can go wrong—except bugs. Be very careful about using this strategy with any user interface. Users do the darndest things!

Return an error code. This is a common strategy, but one with problems. First, everything becomes a function—no Subs allowed. On the other hand, you can say that everything becomes a Sub. No functions are allowed because you’ve already used up the return value. Any value that must be returned has to come back through a reference parameter.

The Component Object Model uses this system. All functions return an HRESULT, which is a 32-bit value containing status bits and a numeric code. Negative HRESULTs are errors. Positive HRESULTs are nonerror results. I’ve programmed these functions in C++, and it’s not very natural or intuitive. Even property get functions have to return values through references. It’s certainly a structured and consistent system, but for Basic…Well, I’d have to call it un-Basic.

There are some less drastic variations. If a function returns only positive values, you can return errors as negative values. Another variation is to reserve one special value as an error return. If you need more information about the error, use the system discussed in the next item. If the return value has a String or Variant type, return an empty string or the Empty value, respectively. If it’s numeric, the special value might be 0, -1, or some other number that is out of range. The Win32 API uses this technique. It’s an inconsistent system because you have to check the documentation of every function to see which values repre­sent errors. Furthermore, the whole scheme depends on finding at least one value you can use to represent “invalid.” If there isn’t one, you’re out of luck.

Provide an error function or property. The Win32 functions indicate in the return value whether an error has occurred, but you have to call GetLastError to see what the error was. You can’t use this function from Visual Basic, but the Err object has a LastDllError property that returns the last error from an API call. You can follow a similar strategy. Define a global Error function that can be called from anywhere, or give your classes an Error property that reports more details about the last error.

This scheme creates a modal system where every procedure call sets a new error mode. As soon as you call another procedure, you move to its error mode and the error code for the last procedure is lost forever. Your implementation also has to set the internal error variable for every procedure—either to an error code or to a no-error code (usually zero).

Raise errors. This is how Visual Basic statements and procedures work, and it’s how all but the rudest of controls work. It’s also the method this book will use as its default. I might use some variations on the strategies discussed above, but mostly I’ll be raising errors from inside methods, properties, and procedures.

This gives the client the most flexibility. They can ignore errors (God forbid) and let Visual Basic handle them. They can use On Error Resume Next to handle errors as if they were invalid function returns, or they can use On Error Goto to send errant behavior to error traps.

The following sections explain techniques for raising errors.