Re: Exceptions, and try...finally
From: Rob Kennedy (me3_at_privacy.net)
Date: 11/20/04
- Next message: Duncan Murdoch: "Re: Exceptions, and try...finally"
- Previous message: Rudy Velthuis: "Re: Adress of an array in record?"
- In reply to:(deleted message) L D Blake: "Re: Exceptions, and try...finally"
- Next in thread: Duncan Murdoch: "Re: Exceptions, and try...finally"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 19 Nov 2004 18:15:25 -0600
L D Blake wrote:
> By the time control is passed to the Finally block, as a subroutine, the
> excepton is effectively erased.
Yet the code between the end of the finally block and the beginning of
the next finally or except block doesn't run. Obviously, there's still
something going on after the finally section "returns" to _HandleFinally
to cause that other code to be skipped. If the exception stopped after
the finally block completed, then a try-except statement wrapping the
try-finally statement would be irrelevant because the finally block
would somehow eat the exception before the except block got a chance to
do anything.
You can look at the assembler code all you want, but if when I run some
Delphi code it doesn't do what you say it will, then either the code
that executed wasn't the code you saw, or your interpretation of the
code was wrong. The Delphi language does not behave the way you say it does.
>> If the try-finally block completes successfully, then the first line
>> after the try-finally block will execute. If there was an exception,
>> then that line will not execute.
>
> That's also not true.
>
> Try-Finally causes a program exit only when it's at first level...
We went over this a month ago, Laura. Try-finally does not cause a
program exit. That's caused by the exception handler installed by the
System unit in _StartExe. You're even the one who pointed that out to
me. (Before that, I though it was an exception handler installed by the
OS, not by the application.)
> if there's
> more to your code --nested trys for example-- the first line after finally is
> always executed.
That depends on what that first line is. I wasn't thinking of nested
blocks when I wrote "first line" before, and I'm not sure of a better
way to put it, so I'll just show you, below.
> try
> try
> //cause exception here
> finally
> cleanup1;
> end;
> finally
> cleanup2;
> end;
>
> Both cleanup calls will be executed whether there's an exception or not.
Insert a line between the inner try-finally statement and the outer
"finally" block. That line will not run.
try
try
// cause exception here
finally
cleanup1;
end;
// If this next line executes, then the previous try-finally block
// completed successfully. But in this case, it won't run.
WillNeverRun;
finally
cleanup2;
end;
>> In Larry's test case, there is an exception object, but it has not yet
>> been retreived from the system's exception record and put in a place
>> where ExceptObject (or AcquireExceptionObject, I suppose) can get at it.
>> That doesn't happen until an "except" block is entered.
>
> Wrong again... Finally DOES NOT create or reference exception objects.
I never said it creates exception objects. What I had suggested, though,
was that after an except block had executed (and re-raised the
exception), a subsequent finally block could gain access to the
exception via the ExceptObject function. That turns out to be false; I'm
not sure why.
>> Only when there was no exception object in the first place. Larry raised
>> his own exception by creating an Exception instance and applying the
>> "raise" keyword to it. In that case, a reference to that object is
>> passed along in the exception record. System.pas's exception-handling
>> code detects that and skips the exception-object-creation code, instead
>> using the already-provided object.
>
> Not in a Finally block... even if there is an exception object from Raise, it
> is destroyed before Finally code begins executing... This is accomplished
> because all _HandleFinally really does is call the exception code as a
> subroutine... no objects or exception pointers are passed.
Consider the following:
type
RKException = class(Exception)
public
destructor Destroy; override;
end;
destructor RKException.Destroy;
begin
inherited;
ShowMessage('Destroyed');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise RKException.Create('Hello, world');
finally
ShowMessage('Inside a finally block');
end;
end;
When you run the above code, you'll get a message box when the exception
is destroyed. The program will actually display three message boxes, in
this order:
1. Inside a finally block
2. Hello, world
3. Destroyed
Clearly, since the exception object's properties and methods are used
after the finally block completes, the exception does not get destroyed
before the finally block executes, which is what you had claimed. (In
case you're wondering, the second message box is displayed by the global
Application object, which catches all exceptions in a VCL program. I
just thought I'd mention that, since I know how foreign the VCL is for you.)
>>>> Any ideas would be most welcome.
>>>
>>> Short of modifying the language... you're screwed.
>>
>> Hardly. Check out my response to Larry.
>
> Yes you can raise an exception in an Except block causing the Finally block to
> be entered with your chosen exception... but that will NOT make the exception
> object available inside the Finally block.
I know. But notice how my code didn't even *have* a finally block -- did
you even look at my code? What Larry was looking for was a way to
execute some code in response to an exception and other code for normal
operation. His attempt at accomplishing that was to try to detect the
presense of an exception object in a finally block, but that obviously
wasn't working. The code I gave should solve the problem. It solves the
problem in his sample code, anyway. If his real code is different, then
he should say so and provide a better sample, but with what he's given
so far, I don't think he really needs a try-finally statement at all.
> It creates a NEW exception object
> that can only be manipulated in the next Except block.
You don't know what you're talking about. There is exactly one exception
object created in my sample code. The "raise" command takes that object
and stores it away in the arguments passed to RaiseException. The OS
then passes those arguments back to the application's exception handler
in the form of a TExceptionRecord. Within the "except" block, the
exception object is the very same object created in the "try" block.
Nothing got destroyed or created in the meantime.
> The only way to get information about the actual first exception in a Finally
> block is to modify the language.
The _HandleFinally procedure has access to the TExceptionRecord that
contains all the information about the exception, including a reference
to the object. You could patch _HandleFinally to jump to your own code,
extract the exception information, and then resume with Borland's
_HandleFinally code. It's tricky, but not impossible. Neither the
compiler nor the System unit would need to be recompiled.
>>> 1) You can have exceptions without exception objects (try / finally)
>>
>> You can have *Windows* exceptions with exception objects. You cannot
>> have Delphi exceptions without exception objects because Delphi
>> exceptions *are* objects. Delphi's "except" clauses only catch Delphi
>> exceptions.
>
> A design error if ever there was one.
How so? The language catches the language's stuff, independent of what
OS is running. And this design even came *before* Kylix was around.
> When windows gets into trouble (div 0, out of memory, etc.) Delphi will
> roundly ignore your Except code unless Sysutils is loaded.
So what? As far as the language is concerned, division by zero is an
OS-specific error. The language should have as little OS-dependent
content as possible (which is why there are frequent complaints about
how Delphi's interfaces come with reference-counting baggage for COM
support). So what if the language requires an adaptor to work with
OS-specific errors?
>>> 2) You can have exception objects without exceptions (Raise, Assert)
>>
>> When you use "raise," there is an OS exception.
>
> No there isn't... there is a user raised exception, done through a call to the
> API function "RaiseException", but the OS is in no way in an exception state
> at that moment... all it's doing is using the SEH mechanism to force the
> Except or Finally block to execute... there is no OS exception at that point.
I guess there must be a blurring of terms here. The OS is certainly
involved in triggering the application's exception code. RaiseException
will notify an attached debugger that an exception has occured, for
instance.
The Delphi implementation enlists the operating system's help in raising
the exception. That way, non-Delphi code can also trap the exception by
using the same SEH structures that Delphi and the OS use.
If you want to reserve the term "OS exception" strictly for things
raised entirely without the application's input, then so be it. But now
I guess we have three concepts when I though we only had two.
Does Windows really have an "exception state"? What things are allowed
to put it into that state? How does it leave that state? In what way
does it behave differently when it is in that state? Can an application
detect which state the OS is in?
-- Rob
- Next message: Duncan Murdoch: "Re: Exceptions, and try...finally"
- Previous message: Rudy Velthuis: "Re: Adress of an array in record?"
- In reply to:(deleted message) L D Blake: "Re: Exceptions, and try...finally"
- Next in thread: Duncan Murdoch: "Re: Exceptions, and try...finally"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]