Reputation: 19
RE: How to correctly write Try..Finally..Except statements?
I'm still confused by the OP's original question. Specifically, the last line of the procedure (outside of the try..finally..end) that reads "Screen.Cursor:=crDefault".
My understanding is that any exceptions raised inside a try..except|finally..end block WILL execute the code after the "end" of the "try".
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor := crDefault;
end;
In the above example, I don't see any reason why "Screen.Cursor:=crDefault" would not be executed. Please correct me if I'm wrong.
As a further example, I've compiled this little bit of code to help illustrate. When the code is ran, THREE (3) ShowMessage() dialogs will be presented. The first "Exception Raised" and the second "finally" and the third "at end".
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
showMessage(format('%s', [12]));
except
showMessage('Exception raised');
end;
finally
showMessage('finally');
end;
showMessage('at end');
end;
So, I'm confused on why his "Screen.Cursor:=crDefault" isn't being ran, in it's original form and code. Can someone please elaborate?
Upvotes: 1
Views: 1430
Reputation: 125749
The code you posted seems to work fine, because you're able to handle all possibilities. Try changing it somewhat though, so that there's an exception raised your code doesn't handle:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
end;
ShowMessage('Got here');
end;
Run this, and you'll see Finally
, then the exception for 42
, but no Got here
message. This is because the exception took you out of the current block, the stack is unwound, and the code from the end
of the finally to the end of the procedure never executes.
Move the final ShowMessage
call from where it is to inside the finally
and run again.
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
You'll now see both calls to ShowMessage
in the finally
block, one after the other, but not the one after the finally
block's end;
. Code inside the finally
block is guaranteed to execute, while code beyond it may or may not.
To make it even more clear, the presence of the try..except
block can be removed:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('42');
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
The entire purpose of the try..finally
block is to ensure that code inside the finally
section will execute before the procedure ends.
Upvotes: 11
Reputation: 181
In Delphi, the finally
block doesn't really handle the exception that occured in the try
block. It only guarantees that the code in the finally
block will always be executed, whether an exception occured or not in the try
block. If an exception really happened in there, it won't get caught. And when an exception didn't get caught, you know what happened to the code below it.
To catch the exception that might be occured, use the try...except...
block instead. You can combine these two construct to do those two action: (1) guarantee the execution some piece of code, and (2) catch the exceptions which might occured. The common usage is like this:
try
try
// do something that might cause an exception.
finally
// do something that must be executed WHATEVER happened.
end;
except
// do something ONLY IF an exception has occured.
end;
So, you should change your code and move the Screen.Cursor := crDefault;
inside the finally
block. In addition, add the try...except...
block to surround the try...finally...
block. Like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
try
// do something.
finally
Obj.Free;
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
Or, if you are not sure that the code Obj := TSomeObject.Create;
is safe enough, you should add the second try...finally...
block to surround it, like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
try
try
Obj := TSomeObject.Create;
try
// do something.
finally
Obj.Free;
end;
finally
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
There, hope it helps :)
Upvotes: 2
Reputation: 8866
You don't actually catch the exception. In this case, upon exception, the "finally" block of code will execute, and then the exception will unwind the stack.
Upvotes: 1