views:

3664

answers:

9

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?

+3  A: 

Take a look at http://www.madshi.net/madExceptDescription.htm

Pierre-Jean Coudert
We're using madExcept already. But I'm looking for a way to find the uncaught exceptions at design time. I have been looking for some kind of code analysis tool that could cover this but have not found any.
ajob
+4  A: 

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!

PatrickvL
+1  A: 

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.

Ralph Rickenbach
If something raises an exception I would say there is a need to catch it, otherwise it will surface as an unhandled exception in runtime. The question is how to secure that all raised exceptions are really handled.
ajob
+4  A: 

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;
Lars Fosdal
+8  A: 

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.

Frederik Slijkerman
+6  A: 

(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...)

Graza
+3  A: 

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.

skamradt
+3  A: 

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.

Jim McKeeth
+1  A: 

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".

Frank Shearar