Question

Is there a good way to find out which exceptions a procedure/function can raise in Delphi (including it's called procedures/functions)?

In Java you always have to declare which exceptions that can be thrown, but this is not the case in Delphi, which could lead to unhandled exceptions.

Are there any code analysis tools that detects unhandled exceptions?

Was it helpful?

Solution

(Edit: It is now obvious that the question referred only to design-time checking.)

New answer:

I cannot state whether there are any tools to check this for you. Pascal Analyzer, for one, does not.

I can tell you, however, that in most Delphi applications, even if there was a tool to check this for you, you would get no results.

Why? Because the main message loop in TApplication.Run() wraps all HandleMessage() calls in an exception handling block, which catches all exception types. Thus you will have implicit/default exception handling around 99.999% of code in most applications. And in most applications, this exception handling will be around 100% of your own code - the 0.001% of code which is not wrapped in exception handling will be the automatically generated code.

If there was a tool available to check this for you, you would need to rewrite Application.run() such that it does not include exception handling.

(Previous answer: The Application.OnException event handler can be assigned to catch all exceptions that aren't handled by other exception handlers. Whilst this is run-time, and thus perhaps not exactly what you are after (it sounds like you want to identify them at design time), it does allow you to trap any exception not handled elsewhere. In conjunction with tools such as the JCLDebug stuff in the Jedi Code Library, you could log a stack trace to find out where & why an exception occurred, which would allow for further investigation and adding specific exception handling or prevention around the guilty code...)

OTHER TIPS

My guess is that you're trying to make Delphi behave like Java, which is not a good approach. I'd advise not to worry too much about unhandled exceptions. In the worst case, they'll bubble up to the generic VCL exception handler and cause a Windows message dialog. In a normal application, they won't halt the application.

Well-written code would document the different exceptions that can be raised so you can handle them in a meaningful way. Catch-all handlers aren't recommended since there is really no way to know what to do if you don't know why an exception was raised. I can also highly recommend madExcept.

Except for a scan on the "raise" keyword, there's no language construct in Delphi that tells the casual reader which exceptions can be expected from a method.

At runtime, one could add a catch-all exception handler in every method, but that's not advisable, as it will slow down the speed of execution. (And it's cumbersome to do too).

Adding an exception-handling block to a method will add a few assembly instructions to it (even when the exception isn't triggered), which forms measureable slow-down when the method is called very often.

There do exist a few libraries that can help you in analyzing runtime exceptions, like madExcept, JclDebug, and EurekaLog. These tools can log all kinds of details about the exception, it's highly advisable to use one of those!

The short answers is there is no tool that does what you say, and even a scan for the raise keyword wouldn't get you there. EAccessViolation or EOutOfMemory are just two of a number of exceptions that could get raised just about anywhere.

One fundamental thing about Delphi is the exceptions are hierarchical: All defined language exceptions descend from Exception, although it is worth noting that it is actually possible to raise any TObject descendant.

If you want to catch every exception that is raised in a particular procedure, just wrap it in a try / except block, but as was mentioned this is not recommended.

// Other code . . . 
try
  SomeProcedure()
except  // BAD IDEA!
  ShowMessage('I caught them all!');
end;

That will catch everything, even instances of a raised TObject. Although I would argue that this is rarely the best course of action. Usually you want to use a try / finally block and then allow your global exception handler (or one final try / except block) to actually handle the exceptions.

I will second (or is it third) MadExcept. I have been using it successfully in several commercial applications without any problems. The nice thing about MadExcept is that it will generate a report for you with a full stack trace that will generally point you in the right direction as to what went wrong, and can even include a screenshot, as well has have this automatically emailed to you from the clients computer with a simple mouse click.

However, you don't want to use this for ALL exceptions, just to catch the ones you miss. For instance, if you open a database and the login fails, it would be better for you to catch and handle this one yourself rather than give the user the MadExcept default error in your application occured message.

Any exception not explicitly or generally handled at a specific level will trickle upwards in the call stack. The Delphi RTL (Run Time Library) will generate a set of different exception classes - (mathematical errors, access errors, class specific errors etc). You can chose to handle them specifically or generally in the different try except blocks.

You don't really need to declare any new exception classes unless you need to propagate a specific functional context with the exception.

As previous commenters wrote, you can also add a mother of all exception handlers like MadExcept or EurekaLog to catch the uncaught.

edit: This is a blanket insurance against unhandled exceptions

try
  ThisFunctionMayFail;
except
  // but it sure won't crash the application
  on e:exception
  do begin
    // something sensible to handle the error 
    // or perhaps log and/or display the the generic e.description message
  end
end;

For runtime try Eurekalog. I do not know whether a tool exists for design time. You will have more dificoulties even when you have third party code without source. There is no need in Delphi to catch exceptions, so you do not have to declare them like in Java.

What I wanted to say is that Delphi does not require that an exception is handled. It will just terminate the program. EurekaLog provides means to log handled and unhandled exceptions and provide a wealth of information on the sate of the program when the exception occured, including the line of code it occured at and the call stack at the time.

As Jim McKeeth points out, you can't get a definitive answer, but it seems to me that one could partially answer the question by some static analysis: given a particular function/procedure, construct a call graph. Check each of the functions in that call graph for a raise statement. That would tell you, for instance, that TIdTcpClient.ReadString can raise an EIdNotConnected (among others).

A clever analyser might also note that some code uses the / operator and include EDivByZero as a possibility, or that some procedure accesses an array and include ERangeError.

That answer's a bit tighter than simply grepping for "raise".

Finalization sections of units can raise exceptions too. These will slip by I think... and are also somewhat problematic.

I think Delphi IDE has a build-in "stack trace" or "stack tree" something like.

This question reminds me of Skybuck's TRussianRoulette game... google it, it's code and answer may help.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top