views:

602

answers:

4

I've set Application.OnException to a custom exception handler so that I can log crashes and give an option to exit. However, I'm now finding that this runs even on exceptions that I've already handled, for instance, exceptions that come up when validating number inputs. Is there a way to have the custom exception handler only run on unhandled exceptions?

Edit: it turns out that I get the expected behavior when I run outside the debugger. Maybe it's just a debugger thing. I do not find the Delphi debugger's interaction with exceptions to be intuitive, to say the least.

+1  A: 

An alternative might be to not use Application.OnException. But to simply catch all exceptions in your "main" function. That way you can catch all exceptions you have not cached before, log the exception, and then crash.

bjarkef
That's just what the Application.OnException method is for.
Smasher
+1  A: 

Application.OnException should only fire for unhandled exceptions. Re-raising an exception in a try-except block will cause the exception to be handled by Application.OnException. For input validation that raises an exception, you could display a message to the user and then re-raise the exception only if you want it recorded into the error log.

C Harmon
The weird thing is that it appeared to be re-raising the exception without my help. But as I edited the question to say, this only happens in the debugger. Weirdness. Thanks though.
Erika
+4  A: 

Quoting from the documentation (Delphi 7) on TApplication.OnException

"Use OnException to change the default behavior that occurs when an exception is not
handled by application code."

So: Only unhandled exception will be available in the OnException event handler. What you are experiencing is probably the Delphi IDE breaking on the exception. This is (at least in Delphi 7) configurable.

In Delphi 7 you can configure it by clicking on the Tools->Debugger Options menu. Then select the "Language Exceptions" an un-ckech the "Stop on Delphi exceptions" checkbox. It might however be different in other delphi versions.

Thomas Watnedal
+8  A: 

If behavior changes inside and outside the debugger, then it's not really your program that's telling you about exceptions. I've written about this phenomenon on my Web site:

Why do I continue getting error messages even after I have written an exception handler?

An excerpt:

In its default settings, the Delphi IDE notifies you whenever an exception occurs in your program ... . What’s important to realize is that at that point, none of your program’s exception-handling code has run yet. It’s all Delphi itself; its special status as a debugger allows it to get first notification of any exception in your program, even before your program knows about it.

After you dismiss Delphi’s message box, execution will be paused at the best line of your source code Delphi could find to represent the source of the exception. Press the “Run” button to have your program resume. Control will pass to the next finally or except block. Before you resume your program, you may use the various debugging tools at your disposal. You can inspect the values of any variables in scope, and you can even modify their values.

So, how do you notify Delphi that you've already handled an exception? You don't — because your program hasn't handled it yet. And why can't the debugger detect whether your program is going to handle an exception? Because in order to do that, it needs to execute your program further. Detecting the lack of an exception handler is like solving the halting problem. The only way to determine whether an exception is going to be handled is to let the program run and then see whether the exception gets handled. But by that point, it's too late to do any debugging, so the debugger has no choice but to pause your program when it first detects an exception and then let you figure out what to do from there.

My article goes on to describe some ways you can avoid having the debugger catch certain exceptions, summarized here:

  • Use advanced breakpoints to temporarily disable breaking on exceptions for certain regions of the code.
  • Configure the debugger to ignore certain classes of exceptions (and their descendants).
  • Tell the debugger not to notify you about any exceptions.
  • Disable integrated debugging altogether.

There's another options that I didn't include in my article:

  • Change your program such that the exception doesn't get raised in the first place.

You say you're validating numeric input. That sounds to me like you're doing something like calling StrToInt and then catching the EConvertError exception when the input isn't a valid integer. That's an expensive way of validating input. Instead of that, use TryStrToInt, which will tell you whether the conversion succeeded, or StrToIntDef, which will silently return a default value instead of raising an exception. Another option is plain old Val, which tries to convert a string, and if it fails, it tells you which position in the string caused the failure. Val is particularly useful if you want to consume as many characters as possible for the conversion and then resume parsing at the next non-numeric character.

Rob Kennedy
Wow, thanks for all the suggestions. The exception does get handled when run through the debugger, I watch it step through the exception handling code, but then it pops out to the generic handler. I will look into the other options for string conversion, that is really the main thing.
Erika
+1 Great answer there, Rob.
robsoft