On swallowing and throwing...

19 July 2006

It seems that it's my destiny to talk about exceptions (see here and here on my personal site, as examples). But because this blog entry is going to be about them and it's right here on my work blog, you can rest assured that I'm going to talk about them now in relation to our products.

I read Framework Design Guidelines by Krzysztof Cwalina and Brad Abrams, architects on the BCL team at Microsoft, a few months back just before I was interviewed for my current job at Developer Express. It's a great compendium of topics on designing a framework or a library that's going to be used by many developers. The thing is, as I realized at the time, it's not just about writing frameworks and libraries for sale, much as Developer Express does. The advice in this book also applies to internal developers who are writing reusable code for their colleagues. By following the conventions and standards what Microsoft have done in the .NET Framework (and by understanding the issues they faced and the solutions they took), the people who will use your framework will find it familiar.

Anyway, one of the points in the book that really struck me was made by Krzysztof: "Each exception can carry two main pieces of information: the exception message explaining to the developer what went wrong and how to fix it and the exception type that should be used by handlers to decide what programmatic action to take." (Page 184) Hold that thought.

I would argue that when we use a library or a framework, we expect it to let us know when things go wrong, when it encounters a situation that it can't handle. Suppose some method would fail if we passed in a null reference. We would expect the method to check and to throw a NullReferenceException. If a file wasn't found, we'd expect to see a FileNotFoundException. And so on.

Sometimes we'd accept the framework wrapping a lower-level exception inside its own exception class. So that, for example, an attempt to open a configuration file might result in a file not found, access denied, etc, type exception, but the framework wraps them all inside an "Unable to open configuration file" exception (and to see the real reason why we can look at the InnerException property).

Now this is where it gets interesting and I need some advice from my readers. Although we do have a couple of frameworks (XPO, and XAF in beta), we're mostly known for UI controls.

So, would you expect a UI control like TextEdit to throw exceptions that it couldn't deal with? Or would you expect the control to perhaps swallow (that is catch and not rethrow) as many as it could, and only let out those it couldn't do anything about? Do you find the thought of possibly bad or catastrophic exceptions in your code being discarded by your control? Maybe you think that this question is a "beginner versus advanced" type topic, in that beginners would be confused by unforeseen exceptions so the more the control discarded the merrier, but that advanced developers already have a sophisticated exception handling subsystem and need to "see" them all (or at least those that are not recoverable).

Let's take a concrete example from our code (this is from the hyperlink editor) so that we can get away from hand-wavy generalizations.

  Process process = new Process();
  process.StartInfo.FileName = (e.EditValue == null ? string.Empty : e.EditValue.ToString());
  process.StartInfo.Verb = "open";
  process.StartInfo.WindowStyle = Properties.BrowserWindowStyle;
  try {
  catch {}

If the process, that is, the browser in this case, doesn't start, for whatever reason, our code just swallows the exception. You, the developer (or the user) don't get to understand why (unless you happen to be running in the IDE and you've activated the "get first-chance exceptions" option); it'll just seem as if the code isn't working. Even worse, the code in the try block may fail due to some entirely different reason, a catastrophic problem like out of memory or a thread abort and again you wouldn't know why. Of course, the entire call stack in the exception is also thrown away, so that you and we would have a much harder problem trying to understand a context-sensitive bug.

My argument here is that, if the catch block were removed, you at least have the choice to do something about an exception that might be raised in this try block. Maybe you don't care about why it fails, just like the code as written, so just add a similar catch block in your code and discard the exception. But if you do care, suddenly you are in a whole better position than before. But either way, you have to do more work.

My questions to you are these:

  • Is this kind of code acceptable in a UI control? Is a UI control just some kind of black-box where we don't care about errors?
  • If it isn't, and we cleaned up this and similar code so that you, as developer, started getting a whole bunch of exceptions, would you know what to do to catch them in your UI layer and properly process them (such as logging them)? Or would it be too disruptive? Would you be ready to have to do a lot more work in your forms?
  • For WinForms controls, that's certainly a problem but it's fairly solvable. What about ASP.NET controls? There your app is running an a server somewhere. There is no UI at the server. If you're not careful, your exceptions get sent to the client as an error page and you know how dumb that can be. But do you know how to catch and process exceptions like that?
  • Given the quote above, I would suppose you'd prefer different exception types for different errors. Or maybe not? Would Exception instances be fine?
  • Are you an advanced developer with a good solution to unhandled exceptions? Do you use something like the Exception Handling Application Block from the Patterns and Practices group at Microsoft?

As you can see, I'm debating whether we should make some pretty major changes to our code, but only if there is support from our customers. Over to you...

Free DevExpress Products – Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We’ll be happy to follow-up.
Lasse Vågsæther Karlsen
There are many ways to handle errors in UI controls, and swallowing the exception is one way.

I have also seen the "Let's post a message box about it" way, which is the worst way anyone could ever think of.

The problem with UI controls is that in some cases it's not my code that has fired the code that threw the exception, but rather some event code in the runtime in response to a keyboard or mouse event. In this case there is no place I can put my own try/catch block in order to swallow or handle it and my application will crash regardless of what I would like to do.

In this case I personally would like to have an event on the form, control, or a separate component, that gets fired to inform me about the problem. If I don't handle the event, the problem is silently swallowed. If I handle the event, I can do exactly what I want.

However, the problem is of course trying to figure out what exceptions are thrown in response to user events and not from calls from my own code, because in calls from my own code I would personally like to have the ability to use the try/catch block.

Perhaps the way I have handled it so far is a good way, just override the appdomain and application exception fallback events and handle it from there, but I get a few false positives.

I guess I don't have any solution either, just don't go with the messagebox idea :)
20 July 2006
Well, I'm not currently one of your customers, so feel free to ignore me.   But I can think of plenty of situations where I'd be royally pissed off if an exception was swallowed like this.  Consider, for example, data-driven controls.  If there's a problem connecting to the database, I don't want the control to just do nothing -- I want an exception to be thrown.

Note that I am an ASP.NET developer -- I don't think the situation is really any different for exceptions thrown on a server instead of a client.  In neither case do I want the raw exception to be seen by the user.  In both cases I need to catch the exception and handle it or log it, and give the user an appropriate message.  This is actually pretty easy to do in ASP.NET, in my opinion.

Empty catch blocks can hide a multitude of sins -- if you remove them and your controls suddenly start throwing (to quote your own text) "a whole bunch of exceptions", that may be a sign that there are problems in the controls themselves.  There may well be many exceptions that should be handled at the control level -- but they need to be handled, not swallowed.

And yes, I'd prefer different exception types for different errors.
20 July 2006
Scott Blood
This is a really interesting subject, one of which affects all developers, either the experienced or the novice.

What we have to remember here is that we are writing applications that we want users to pay for and everytime your application throws an error at them, they might not necessarily understand what is going on and as has been seen manytimes before, discount your software as unstable or unuseable and change there provider.

Now one good solution for me as a developer is to  have some kind of global sink for errors and from there i can respond to errors on a global platform.

For example, i use the DevExpress XtraScheduler to display and manage a holiday calendar for users, now if for some reason either the data provider for this calendar becomes unavailable or the calendar throws some kind of internal exception that i cannot solve, the only option is to close down the application and restart it, but is this acceptable to me or my users??????

In a UI control, i would expect an errors that can easily  be resolved to be fixed by the User control, so if, for example, the XtraBars cannot find BonusSkins Assembly, should i worry about this, well not really.  In my honest opinion, exceptions that occur that can be resolved by the component, should be resolved by the component, i dont have the time nor the patience to pick up every exception and handle it for a component i have paid $$$ for.

On the other hand though, would a missing file, such as mentioned above, be a fault of my installation procedures or the end user deleting things they shouldnt, it really is a game of cat and mouse, do we , dont we?????

Exceptions are good and its handy to be able to log none critical exceptions so that in future versions of an applicaiton, you can either erradicate these or throw a try catch around them, so that they can be cleaned up, because remember, everytime something happens inside your code that is not handled, this is a chance for a crafty developer or virus to attatch to something (i.e. the memory space or stack) and actually cause damage, such as can be seen in many microsoft products.

So to conclude, yes it would be good to get notification of all Exceptions that occur, but maybe not actually thrown as an exception, maybe an event in a single global component from which i can decide what i want to happen.

21 July 2006
First let me state that my expertise lies in the database domain (aka DBA).  In that tier I expect every single "error/exception" to be thrown.

But for the past 18 months I have been doing UI work using the DevExpress .NET suite. (Win Forms)

One of the first "swallowed" expections I found was the FormatString in the SummaryTotal in the XtraGrid control.  From a debugging persepctive, it is a catch22.  Yes, I need to know it is wrong, but I do not want the entire grid control to choke just for this "little" error.  Personally I like this behaviour in this particular situation.

When I am writing middle tier code, I give the caller the option of having any errors rethrown or swallowed...

Perhaps this is the middle ground.. A simple boolean flag indicating to either consume or throw the exception...

23 July 2006
John Gooding
Catching and swallowing a known circumstance, say file not found, and then providing a handler that creates the file (totally ignoring the fact that you should do a check if exists to avoid the exception in the first place) and creating it for the user with a set of default values (again as long as it is documented behavior), swallowing the exception is acceptable.

Catching all errors indiscriminately with a catch {} is plain and simply wrong, there is nothing positive or good about this situation.  A determination by the library/control writer on what a critical error has to be made and the correct action must be applied, either provide default behavior, throw an error, or swallow the error and return a failed result to the developer.

The developer is expecting their calls into your library/control to do an action when they set a property, call a method or respond to an event.  If this behavior is not honored and they are not notified when it fails, then they can not provide the functionality and reliability to their users.

These rules of behavior have to be combined with the most important word of all consistency, each method, property, control should behave the same in similar methods at all times so that the developer isn’t writing try/catch here and checking results somewhere else, and counting on default behavior else where for similar activities.
25 July 2006

A boolean flag is good. And it could be more flexible if a "log engine" is notified in the catch statement. I have been using log4net, but I believe there are something very similar built in to .NET. So if logging is turned off by a configuration file, the message gets swallowed or perhaps also rethrown depending on the boolean flag. And then you could have a custom log appender that listens to the "log engine" and decides what to do about the message or exception. Or perhaps it is more simple with an event after all...But the possibility to configure at run time is nice and the different log levels fits very well. Fatal, Error, Warn, Info and Debug in log4net.
25 July 2006
Time again I think for you to take another look inside the CTO's brain to see what he's considering for...
17 August 2006

Please login or register to post comments.