Fog Creek Software
Discussion Board




Exceptions in VB

Joel,

As far as I am aware, CityDesk is (at least partly) written in VB 6.

As such, I am interested in how you manage exceptions / errors in VB 6.

For example, you state:

<quote>
A better alternative is to have your functions return error values when things go wrong, and to deal with these explicitly, no matter how verbose it might be.
</quote>

This is an interesting statement in the context of VB 6, since the Err object in VB 6 is essentially a wrapper of a COM HRESULT which is (in essence) an return error value.

Thus, in VB 6, using a separate return value is (IMHO) silly since there is one (Err object / HRESULT) there for you built in ready to use...

You may then argue - 'they are not the same since you can't use code like in my example'

<quote>
T tmp;
if (ERROR == g(x, tmp))
    errorhandling;
if (ERROR == f(tmp, result))
    errorhandling;
</quote>

You could do something very similar using On Error Resume Next and checking Err.Number (rather than the return value of g or f) after calls to f or g. Not that I recommend it, of course, but you could it.

All in all, in VB 6 I believe that using exceptions (ie Err.Raise) has just as much flexibility in the 'inline error checking' paradigm you prefer (even though I don't fully agree with your paradigm) and support for other paradigms as well (On Error Goto ErrorHandler etc).

Comments?

Seeya

Matthew
Monday, October 13, 2003

Here's a bug if you use the exception-throwing model.

void AddEntry(string name, string data1, string data2)
{
  Entry entry = new Entry;
  hashTable[name] = entry;
  entry.Initialize(data1, data2);
}

Can you spot it?  (Assume that hashTable is a HashTable and that the method is enclosed in a suitable class.)

Raymond Chen
Monday, October 13, 2003

Raymond,

I am not sure how that is relevant, given that it isn't VB 6. ;-)

Regardless, it might be more useful (given Joel's recent post) to show how it could have been done BETTER using result codes rather than exceptions...

Seeya

Matthew
Monday, October 13, 2003

Raymond,

I don't see your bug, but I'd probably suggest calling the entry.Initialize() method before attempting to add the new entry to the Hashtable. 

That way you would avoid storing an “uninitialized” entry in the Hashtable if an exception was thrown as a result of the call to the entry.Initialize() method.

Guy Incognito
Tuesday, October 14, 2003

entry is already assigned, so when you store it in hashTable doesn't matter as you are operating on the same instance anyway.

Thomas Eyde
Tuesday, October 14, 2003

Well,

Entry entry = new Entry;

doesn't make sense, as the result from "new" must get shoved into a pointer-like thingy.

Of course, what that "pointer-like thingy" is makes all the difference.

If it's an Entry *, then you will leak if an exception gets thrown in the wrong place.  That's why C++ programmers use smart pointers.

David Jones
Tuesday, October 14, 2003


If entry.Initialize() blows up and an exception is caught by the client calling AddEntry(), so that program execution is not aborted, there is now an item in the Hashtable with the key "name", but without the intended data "data1" and "data2". 

GuyIncognito
Tuesday, October 14, 2003

Sorry about the C# code. Yes, the bug is that a badly-placed exception leaves the data structures inconsistent. Since exceptions are basically invisible, people do not naturally consider the consequences of an exception leaving a block of code. (Code you don't write is code you don't think about.)

Raymond Chen
Tuesday, October 14, 2003

Raymond Chen, what a pleasure! I did like your post about "the default answer to every messagebox being cancel" very much and it made as much a delightful reading as Scott Berkun and Joel do with their user interface mantras. I also noticed we have a few authors here apart from Joel himself. Man! This is paradise online.

Gasp!

Matthew, what Joel speaks of in this article might perhaps reflect his choice rather than his agnosticism as against exception handling. I think its a matter of comfort and choice rather than a matter of religious orthodoxy. While both, raising an error from within the module containing the called function as well as providing an inline error handler within the routine provide a solution, the issue is about the risk you'd run into with the first approach - exception handling and how comfortably you have reharsed with the mitigation.

Only that you might discount my feeble opinion as lacking solidarity, let me point out to you a simple case of opening a recordset in Visual Basic 6 as an example. Just to initialize a recordset within a routine, you could have a lot many things to care about. Whether the end of file or begining of file bookmarks have been reached, an invalid connection state while opening the recordset as in opening an already open recordset or attempting to close an already closed one before issuing the open statement, a wrong syntax in the SQL query, or the provider not available on the target computer (especially if your recordset was on a remote machine), or the cursor and lock combination you specified did not support bookmars when you queried the Recordcount property, it flamed back at you.

And it gets even crazier if you were initializing a data environment's recordcount and you happened to be closing an already closed recordset. If the recordset was a select statement without a where clause, and you did not have to provide any parameters, it was all ok. But if you did have to provide parameter arguments to be filled in the where clause and if these parameter values where changed within the procedure after the first call to the open function of the recordset, you'd most definetely find yourself in hot water.

I am sure you'd be very familiar with 3021, 3074, 3075, 3076, 3061 etc.

If you did provide an inline error handler, you'd anticipate each of these situations inside it and act accordingly. For instance, opening an already open recordset, you might just ignore the open statement and write a Resume Next in this if construct in the error handler. On the other hand, not writing an error handler at all passes the HRESULT to the stack of the calling method where the value is also coerced by the calling routine. If you are a forgetful guy like me, you would have got an Overflow error from the calling routine for all your ADO sins where you forgot to provide an inline error handler in the called routine.

No! All of this you'd argue can be taken care of by a few Ifs and Buts in the programming constructs. Sure they can. And one evolves in time to better his coding practices and I guess that is what Joel was talking about rather than the lacunae in one method when compared to the other.

Sathyaish Chakravarthy
Tuesday, October 14, 2003

Yup, the problem I see with this code:

void AddEntry(string name, string data1, string data2)
{
    Entry entry = new Entry;
    hashTable[name] = entry;
    entry.Initialize(data1, data2);
}

is that you're initialising the entry *after* you add it. This is better:

void AddEntry(string name, string data1, string data2)
{
    Entry entry = new Entry;
    entry.Initialize(data1, data2);
    hashTable[name] = entry;
}

That way you won't end up with a bad entry in your hashtable. Doing otherwise is just bad coding.

Not that this has much to do with VB6 though...

Keith Gaughan
Wednesday, October 15, 2003

*  Recent Topics

*  Fog Creek Home