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.
It is up to you to ensure that the application is recovered into a stable state after catching the exception. Usually it is achieved by "forgetting" whatever operation or change(s) produced the exception, and starting afresh on a higher level.
To resolve the exception it will in many cases mean first modifying the data. You should resolve the problem via your online account using the onscreen buttons (and not by e-mail). There are a number of standard options (as shown on the screen shot above): MODIFY ORDER, CONFIRM, or CANCEL ORDER.
If an exception is thrown and not caught (operationally, an exception is thrown when there is no applicable handler specified), the uncaught exception is handled by the runtime; the routine that does this is called the uncaught exception handler.
exception [-d | +d] Command Use the exception command to display an exception's type at any time during debugging. If you use the exception command without an option, the type shown is determined by the setting of the dbx environment variable output_dynamic_type: If it is set to on, the derived type is shown.
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:
There's another options that I didn't include in my article:
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With