Generally I make my exception handlers handle only very specific exceptions, so that the code doesn't try to recover from exceptions that weren't anticipated and potentially aren't recoverable.
But there are a few places where it is impossible to know ahead of time what exceptions could legitimately come up from which recovery is perfectly possible (because of calls to libraries that don't specify what exceptions they might raise). So in these special cases I do in fact trap all exceptions: on e:Exception ....
But this also handles EAssertionFailed exceptions, which really should never be handled because they imply incorrect code.
So I've started writing exception handlers like this:
on e:Exception do
begin
if e is EAssertionFailed then
begin
raise;
end;
…
This just seems ugly and error-prone. (What if I forget the if e is EAssertionFailed code?)
Is there a better way? (Should I just never ever use on e:Exception …, except in a top level handler that aborts the program?)
Update
I think David's comment below is correct - never use a blanket exception handler (other than to log the exception and stop the program).
The problem then becomes "How can I get a list of all exception types that are safe to handle arising from some arbitrary bit of code?" I don't think this is one that Stack Overflow can answer.
(The particular problem is a 3rd party library that reads a configuration file. There are fairly obvious exceptions for filing system errors, but the library seems to raise numerous and almost random exception types for syntax errors within the file itself. I don't think I could ever be sure that I've exhausted the possibilities. I certainly cannot have my application fall over because the configuration file couldn't be read!)
The standard Delphi exception handling pattern is
try
// Some code which may raise an exception
except
on E: SomeError do begin
// Handle SomeError and derived classes
end;
on E: SomeOtherError do begin
// Handle SomeOtherError and derived classes
end;
...
end;
it does not require the use of on E:Exception ….
If you don't know the exception type you should not handle it; or sometimes (ex in DLL code) you should handle ALL exceptions.
Why don't to use this:
try
...
except
on EAssertionFailed do raise;
on E: .... do ... ; // your exception
on E: .... do ... ; // your exception
on E: .... do ... ; // your exception
on E: Exception do ... ; // all other exceptions
end;
If any of the handlers in the exception block matches the exception, control passes to the first such handler. An exception handler 'matches' an exception just in case the type in the handler is the class of the exception or an ancestor of that class.
Or you can remove on EAssertionFailed do raise; and on E: Exception do ... ; from the exception block and then exception handler will be searched in the next-most-recently entered try...except statement that has not yet exited.
I'm offering my own answer here to see what the community thinks.
The point wasn't a misunderstanding of how exception handling works in Delphi. I've summarised the particular problem in my update to the question.
My solution is going to be to convert the many different exception types the library can raise into a single type that I can handle explicitly. I'll put a blanket exception handler around the one problem call to the library. This handler will raise a new exception of a known type, to be caught explicitly higher up at the appropriate time.
(And no, the exceptions raised by the library call do not have any common base type other than Exception itself, so I really do have to catch all exceptions if I want to recover from a failure in the library call.)
Crucially, I believe that although I cannot hope to enumerate all the exceptions that this library call could raise, I can be sure that it won't put the application into an unstable state when it raises an exception.
Update
In the end I was able to use something like this:
on e:Exception do
begin
if (e.ClassType = Exception) or (e is EConvertError) or
(e is EVariantError) then
begin
ShowMessage('The application settings could not be loaded.');
end
else
begin
// An exception we didn't anticipate. Assume it is fatal.
raise;
end;
end;
A lot of the recoverable exceptions raised by the library are of type Exception, but note that only Exception and the specified descendants are handled. The others are raised indirectly by the runtime library. There might turn out to be other exceptions that should be handled too, but this seems like a good start. It's better to have the application bail out when it needn't have done so than to continue running and corrupt the user's data.
Related
The below code works fine, the calc... generates an exception, comment it out or change calc... to not throw and exception and the test fails.
StartExpectingException(exception);
calcMembersPIPEndDate(EncodeDate(2005,01,01),true);
StopExpectingException('calcMembersPIPEndDate - 1st after aDay');
My problem is that any checks I put in this test method after this do not execute.
so
checkEquals(1,0);
StartExpectingException(exception);
calcMembersPIPEndDate(EncodeDate(2005,01,01),true);
StopExpectingException('calcMembersPIPEndDate - 1st after aDay');
fails on the 1st checkEquals
StartExpectingException(exception);
calcMembersPIPEndDate(EncodeDate(2005,01,01),true);
StopExpectingException('calcMembersPIPEndDate - 1st after aDay');
checkEquals(1,0);
passes - why?
I have tried to work out what version of Dunit I am using:
testframework.pas has the following - which didn't seem to
rcs_id: string = '#(#)$Id: TestFramework.pas,v 1.117 2006/07/19 02:45:55
rcs_version : string = '$Revision: 1.117 $';
versioninfo.inc
ReleaseNo : array[1..3] of Integer
= (9,2,1);
ReleaseStr = '9.2.1';
ReleaseWhen : array[1..6] of Integer
= (2005,09,25,17,30,00);
These two methods, StartExpectingException and StopExpectingException are not meant to be called directly.
Instead you are supposed to use the ExpectedException property. When you set this property, StartExpectingException is called. Whilst you could call StartExpectingException I belive that the intended usage is that you assign to ExpectedException.
As for StopExpectingException, you don't call it. The framework calls it. It does so in TTestCase.RunTest, the framework code that executes your test method.
So your test case code might look like this:
ExpectedException := ESomeException;
raise ESomeException.Create(...);
When you state that you are expecting an exception, what you are saying is that your test method will raise that exception. Since raising an exception alters control flow, code that appears after the exception is raised will not execute. Exceptions propagate up the call stack until they are caught. The framework will catch the exception in TTestCase.RunTest. If you have indicated that the caught exception is expected then the test will pass, otherwise failure is recorded.
The net result of all this is that the ExpectedException mechanism can be used if the final act of the test method is to raise that expected exception. The ExpectedException mechanism is no use at all if you want to perform further tests after the exception is raised. If you wish to do that then you should either:
Write your own exception handling code, in your test method, that checks that exceptions are raised as designed.
Use CheckException.
StopExpectingException cannot work the way you expect. It's important to understand the flow of execution in an exception state to see why.
Consider the following code:
procedure InnerStep(ARaiseException);
begin
Writeln('Begin');
if ARaiseException then
raise Exception.Create('Watch what happens now');
Writeln('End');
end;
procedure OuterStep;
begin
try
InnerStep(False); //1
InnerStep(True); //2
InnerStep(False); //3
except
//Do something because of exception
raise;
end;
end;
When you call OuterStep above, line //2 will raise an exception inside InnerStep. Now whenever an exception is raised:
The instruction pointer jumps out of each method (a little like goto) to the first except or finally block found in the call-stack.
Writeln('End'); will not be called.
Line //3 will not be called.
Whatever code exists in the except block of OuterStep is executed next.
And finally when raise; is called, the exception is re-raised and the instruction pointer jumps to the next except or finally block.
Note also that like raise; any other exception within the except block will also jump out, (effectively hiding the first exception).
So when you write:
StartExpectingException(...);
DoSomething();
StopExpectingException(...);
There are 2 possibilities:
DoSomething raises an exception and StopExpectingException is never called.
DoSomething doesn't raise an exception and when StopExpectingException is called there is no exception.
David has explained that the DUnit framework calls StopExpectingException for you. But you may be wondering how to approach your test case checking multiple exception scenarios.
Option 1
Write smaller tests.
You know that's what everyone says you're supposed to do in any case right? :)
E.g.
procedure MyTests.TestBadCase1;
begin
ExpectedException := ESomethingBadHappened;
DoSomething('Bad1');
//Nothing to do. Exception should be raised, so any more code would
//be pointless.
//If exception is NOT raised, test will exit 'normally', and
//framework will fail the test when it detects that the expected
//exception was not raised.
end;
procedure MyTests.TestBadCase2;
begin
ExpectedException := ESomethingBadHappened;
DoSomething('Bad2');
end;
procedure MyTests.TestGoodCase;
begin
DoSomething('Good');
//Good case does not (or should not) raise an exception.
//So now you can check results or expected state change.
end;
Option 2
As David has suggested, you can write your own exception handling inside your test. But you'll note that it can get a little messy, and you'll probably prefer option 1 in most cases. Especially when you have the added benefit that distinctly named tests make it easier to identify exactly what went wrong.
procedure MyTests.TestMultipleBadCasesInTheSameTest;
begin
try
DoSomething('Bad1');
//This time, although you're expecting an exception and lines
//here shouldn't be executed:
//**You've taken on the responsibility** of checking that an
//exception is raised. So **if** the next line is called, the
//expected exception **DID NOT HAPPEN**!
Fail('Expected exception for case 1 not raised');
except
//Swallow the expected exception only!
on ESomethingBadHappened do;
//One of the few times doing nothing and simply swallowing an
//exception is the right thing to do.
//NOTE: Any other exception will escape the test and be reported
//as an error by DUnit
end;
try
DoSomething('Bad2');
Fail('Expected exception for case 2 not raised');
except
on E: ESomethingBadHappened do
CheckEquals('ExpectedErrorMessage', E.Message);
//One advantage of the manual checking is that you can check
//specific attributes of the exception object.
//You could also check objects used in the DoSomething method
//e.g. to ensure state is rolled back correctly as a result of
//the error.
end;
end;
NB! NB! Something very important to note in option 2. You need to be careful about what exception class you swallow. DUnit's Fail() method raises an ETestFailure exception to report to the framework that the test failed. And you wouldn't want to accidentally swallow the exception that's going to trigger the test failure for expected exception.
The subtle issues related exception testing make it important to: test first, ensure you have the correct failure, and only then implement the production code change to get a pass. The process will significantly reduce the chances of a dud test.
I was wondering if there are exceptions/errors, which do make your code jump into an except block but aren't handled by E : exception.
try
i := StrToInt(s);
{...do a lot more...}
except
on E : EConvertError do begin
ShowMessage('You need to input a valid number.');
end;
on E : Exception do begin
ShowMessage('Something went wrong.');
Raise;
end;
end;
Is there a way that a program could have an error that would ignore both statements in this except block?
Or should I do it like this :
try
i := StrToInt(s);
{...do a lot more...}
except
on E : EConvertError do begin
ShowMessage('You need to input a valid number.');
end;
else begin // swapped on e : Exception with else
ShowMessage('Something went wrong.');
Raise;
end;
end;
You can throw anything that derives from TObject. In order to catch every such class you'd need to specify TObject in your on statement.
From the documentation:
Exception types are declared just like other classes. In fact, it is possible to use an instance of any class as an exception, but it is recommended that exceptions be derived from the SysUtils.Exception class defined in SysUtils.
In reality I know of no code that throws anything that does not derive from Exception, although #TLama points out one example in a legacy deprecated VCL class in the comments. Certainly StrToInt only throws Exception descendents.
If you don't need to access the exception object you can use a plain except clause without an on statement.
try
....
except
// deal with all exceptions
end;
Or you can use the else clause of an on statement as a catch all, again you won't get immediate access to the exception object.
Otherwise you can specify a base class for the exceptions that you wish to catch. For instance on E: TObject catches everything derived from TObject.
So, as we see, it is possible that things not derived from Exception may be thrown. But you must ask yourself if your code ever does that? If not then it makes most sense to test for Exception in your catch all handlers. That will give you access to the members of Exception. Of course, one does wonder why you have a catch all exception handler. Their existence are often indication of poor design.
Continuing the theme of StrToInt, you only need to catch EConvertError. That's what is raised when the conversion fails. You should ignore any other exception class as, in your example, the code won't know what to do with anything else. One of the goals of writing exception handling code is to handle what you know how to deal with, and ignore everything else.
In fact, TryStrToInt is what you need here:
if TryStrToInt(s, i) then
// do stuff with i
else
// deal with conversion error
This obviates the need to handle any exceptions and makes your code far more readable.
I know that StrToInt is just an example, but it serves quite well to demonstrate the benefits of trying to avoid handling exceptions.
Consider this part of code:
Try
arqTXT.LoadFromFile(LogPath);
finally
ShowMessage(" The log file could not be found. Check if the service is running. ");
end;
After my message, a system message appears with the message: Cannot open file "C:\log.txt". Is there a way to not show this system message?
I'm using Delphi 2010
Thanks.
You would like to trap that specific exception (EInOutError if I remember correctly) and let other exceptions that occurred to remain unmuted. So if you get some other exception (e.g. EOutOfMemory) if wont get "swallowed" leaving you with seemingly working but broken code.
try
..
except
on E: EInOutError do
begin
..
end;
end;
You can have many exceptions handlers in one construct. Any unhandled exceptions will continue to propagate outside of the try .. except block, until caught elsewhere (e.g. by default exception handler that shows the error box).
There's a couple of things to point out. As other answers/comments explain, you should be wrapping your code inside of a try..except block instead of a try..finally block. I'm not understanding how your code example is supposed to work, because as it's currently written, your message will always show, whether there was an exception or not. It should look something more like...
try
arqTXT.LoadFromFile(LogPath);
except
on E: EInOutError do begin
ShowMessage('The log file could not be found. Check if the service is running. Message: '
+ E.Message);
end
end;
That code will catch and handle I/O exceptions. You would use a different exception type instead of EInOutError to handle different types of exceptions, or the dirty way is to use on E: Exception to catch all types. You can also identify the specific error code using GetLastError and further recognize / log this error code to know exactly what's wrong (File missing, read-only, etc.), assuming that an I/O exception has occured.
Here's a good article explaining how to handle exceptions in Delphi.
The other thing to point out is when you see the exceptions. When you're in Debug mode (running application from the IDE), it will show all exceptions by default, even the ones that you don't see when your application runs on its own. You can disable this in the IDE. That article is for Delphi 2007, but I'm pretty sure it should apply for 2010 as well, because the same options are in Delphi XE2.
Solve it using
try
exception
on e: exception do
ShowMessage("My Message");
Instead of Try/Finally.
There is an example which illustrates my question:
procedure Test;
begin
try
ShowMessage('1');
raise EWarning.Create(12345, 'Warning: something is happens!');
ShowMessage('2');
except
on E: EWarning do
if IWantToContinue(E.ErrorCode) then
E.SkipThisWarning // shows '1' then '2'
else
E.StopExecution; // shows '1'
end;
end;
function IWantToContinue(const ErrorCode: Integer): Boolean;
begin
//...
end;
I tried to use something like this:
asm
jmp ExceptAddr
end;
but it's wont' work...
Any ideas?
Thanks.
No, it is not possible:
There are two kinds of exceptions: logic exceptions that are raised by the programmer using the raise command, and external exceptions that are initiated by the CPU for various conditions: division by zero, stack overflow, access violation. For the first kind, the logic exceptions, there's nothing you can do because they're part of the application "flow". You can't mess with the flow of 3rd party code, you can't even mess with the flow of your own code.
External exceptions
Those are normally raised as a consequence of running a single CPU instruction, when that instruction fails. In Delphi those are made available as EExternal descendants. The list includes access violations, division by zero, stack overflow, privileged instruction and not-so-many others. Theoretically, for some of those exceptions, the causing condition of the exception could be removed and the single CPU instruction retried, allowing the original code to continue as if no error happened. For example SOME access violations might be "fixed" by actually mapping a page of RAM at the address where the error occurred.
There are provisions in the SEH (Structured Exception Handling) mechanism provided by Windows for dealing with such retry-able errors, and Delphi is using SEH under the hood. Unfortunately Delphi doesn't expose the required elements to make this easily accessible, so using them would be very difficult if not impossible. None the less, for particular types of EExternal errors, smart Delphinians might attempt writing custom SEH code and get things working. If that's the case, please ask a new question mentioning the particular type of error you're getting plus the steps you'd like to take to remove the error condition: you'll either get some working code or a customized explanation of why your idea would not work.
Logic exceptions initiated through the use of raise
Most exceptions fall into this category, because most code will check it's inputs before doing potentially dangerous low level stuff. For example, when trying to access an invalid index in a TList, the index would be checked and an invalid index exception raised before attempting to access the requested index. Without the check, accessing the invalid index would either return invalid data or raise an Access Violation. Both of those conditions would be very hard to track errors, so the invalid index exception is a very good thing. For the sake of this question, even if code were allowed to access an invalid index, causing an Access Violation, it would be impossible to "fix" the code and continue, because there's no way to guess what the correct index should be.
In other words, fixing "logic" exceptions doesn't work, shouldn't work, and it's insanely dangerous. If the code that raises the error is yours then you can simply restructure it to NOT raise exceptions for warnings. If that's not your code, then continuing the exception falls into the "insanely dangerous" category (not to mention it's technically not possible). When looking at already written code, ask yourself: would the code behave properly if the raise Exeption were replaced with ShowMessage? The answer should mostly be "NO, the code would fail anyway". For the very rare, very wrong case of 3rd party code that raises an exception for no good reason, you may ask for specific help on patching the code at run-time to NEVER raise the exception.
Here's what could be in some 3rd party code:
function ThirdPartyCode(A, B: Integer): Integer;
begin
if B = 0 then raise Exception.Create('Division by zero is not possible, you called ThirdPartyCode with B=0!');
Result := A div B;
end;
It should be obvious that continuing that code after the exception is not going to allow stuff to "self heal".
Third party code might also look like this:
procedure DoSomeStuff;
begin
if SomeCondition then
begin
// do useful stuff
end
else
raise Exception.Create('Ooops.');
end;
Where would that code "continue"? Quite obviously not the "do usefull stuff" part, unless the code is specifically designed that way.
Those were, of course, simple examples only scratching the surface. From a technical perspective, "continuing" after an exception as you're suggesting is a lot more difficult then jumping to the address of the error. Method calls use stack space to set up local variables. That space was released in the process of "rolling back" after the error, on the way to your exception handler. finally blocks were executed, possibly de-allocating resources where needed. Jumping back to the originating address would be very wrong, because the calling code no longer has what it expects on stack, it's local variables are no longer what they're ment to be.
If it's your code that's raising the exception
Your code can easily be fixed. Use something like this:
procedure Warning(const ErrorText:string);
begin
if not UserWantsToContinue(ErrorText) then
raise Exception.Create(ErrorText);
end;
// in your raising code, replace:
raise Exception.Create('Some Text');
// with:
Warning('Some Text');
AFAIK no. You have to restructure your code to something like
procedure Test;
begin
ShowMessage('1');
try
raise EWarning.Create(12345, 'Warning: something is happens!');
except
on E: EWarning do
if IWantToContinue(E.ErrorCode) then
// shows '1' then '2'
else
raise; // shows '1'
end;
ShowMessage('2');
end;
In C++, it is possible using an SEH __try/__except block whose __except expression evaluates to EXCEPTION_CONTINUE_EXECUTION.
In Delphi, it is not possible to use SEH directly, AFAIK.
BASICally, your code wont work because ExceptAddr is a function, not a variable. So, your code snippet changes as follows:
{$O-}
procedure TForm1.FormCreate(Sender: TObject);
begin
try
OutputDebugString('entering');
raise Exception.Create('Error Message');
OutputDebugString('leaving');
except on E: Exception do
begin
OutputDebugString(PChar(Format('ExceptAddr = %p', [ExceptAddr])));
asm
CALL ExceptAddr
JMP EAX
end;
end;
end;
end;
..you could nest multiple try-except and determine if continue inside each exception:
try
try
some code
except
ret := message('want to continue?');
if ret <> mrYes then
exit;
end;
some other code to perform when no exception or user choose to continue
except
etc..
end;
In some Delphi 7 code I am maintaining, I've noticed a lot of instances of the following:
with ADOQuery1 do begin
// .. fill out sql.text, etc
try
execSQL;
except
raise;
end;
end;
It seems to me that these try blocks could be removed, since they do nothing. However, I am wary of possible subtle side-effects..
Can anyone think of any instances in which these blocks could actually do anything that wouldn't happen without them there?
In this context, the raise operation has no effect and should be removed becuase its simply re-raising the exception that the exception block just caught. raise is typically used to transfer control to the end of the block when no appropriate error handling is available. In the following we handle the custom exception, but any other exception should be handled elsewhere.
try
someOperation;
except
on e: ECustomException do
SomeCustomHandelr;
else
begin
// the raise is only useful to rethrow the exception to an encompasing
// handler. In this case after I have called my logger code. as Rob
// mentioned this can be omitted if you arent handling anything because
// the compiler will simply jump you to the next block if there is no
// else.
LogUnexpectedException('some operation failed',e);
raise;
end;
end;
Be careful that there is similar looking form without the "raise" that DOES have the side effect of eating/hiding any exceptions. practicies by very unscrupulous developers who have hopefully moved on to positions with the competition.
with ADOQuery1 do begin
// .. fill out sql.text, etc
try
execSQL;
except
// no handler so this just eats any "errors"
end;
Removing the except code in above code snippet will make no difference. You can (and I believe you should since it is reducing the readability) remove it.
Okay, really two questions here.
First, it is meaningful: if execSQL throws an exception, it's caught by the try block and forwarded to the except. Then it's forwarded on by the raise to the next higher block.
Second, is it useful? Probably not. It almost certainly is the result of one of three things:
Someone with pointy hair wrote a coding standard that said "all operations that can throw an exception must be in a try block."
Someone meant to come back and turn the exceptions made by the execSQL statment into some other, more meaningful, exception.
Someone new wasn't aware that what they'd written was isomorphic to letting the uter environment worry about the exception, and so thought they must forward it.
I may have answered a bit fast, see at the end...
Like it is, it is useless for the application.
Period!
Now on the "why" side. It may be to standardize the exceptions handling if there /was/will be/is in other places/ some kind of logging code inserted before the raise:
try
execSQL;
except
// Log Exception..
on E: Exception do
begin
LogTrace(Format('%s: Exception Message[%s]',[methodname, E.Message]));
raise;
end;
end;
or for Cleanup code:
try
execSQL;
except
//some FreeAndNil..
raise;
end;
Update: There would be 1 case where I would see some use just like it is...
... to be able to put a Breakpoint on the raise line, to get a chance to see what's going on in the context on that block of code.
This code does nothing, other than to allow the original programmer to place a breakpoint on the 'Raise' and to see the exception closer in the source to its possible cause. In that sense it a perfectly reasonable debugging technique.
Actually, I should posted this as comment to François's answers, but I don't know is it possible to insert formatted code there :( So I'm posting this as answer.
2mghie:
The second one is completely unidiomatic, one would use finally instead.
No, "finally" will cleanup object always. "Except" - only on exception. Consider the case of function, which creates, fills and return an object:
function CreateObj: TSomeObj;
begin
Result := TSomeObj.Create;
try
... // do something with Result: load data, fill props, etc.
except
FreeAndNil(Result); // oops: bad things happened. Free object to avoid leak.
raise;
end;
end;
If you put "finally" there - function will return nil always. If you omit "try" block at all - there will be resources leak in case of exception in "...".
P.S. Of course, you can use "finally" and check ExceptObj, but... isn't that ugly?
The title contains quite a broad question, while its explanation gives a more specific example. So, my answering to the question as how it proceeds from the example, can doubtly add anything useful to what has already been said here.
But, maybe Blorgbeard indeed wants to know whether it is at all meaningful to try ... except raise; end. In Delphi 7, if I recollect correctly, Exit would trigger the finally part of a try-finally block (as if it were some sort of exception). Someone might consider such behaviour inappropriate for their task, and using the construction in question is quite a workaround.
Only it would still be strange to use a single raise; there, but then we should have talked about usefulness rather than meaningfulness, as Charlie has neatly observed.
This code does nothing except re-raising an exception that will allready be raised without this try except block. You can safely remove it.