Found that exception handling in Delphi Tokyo behaves a little different than in previous Delphi versions.
function FuncTest: integer;
begin
Result := 1;
try
raise Exception.Create('Error Message');
finally
Result := 2;
end;
end;
function Test:integer;
begin
Result:=0;
try
Result:=FuncTest;
finally
ShowMessage(Result.ToString);
end;
end;
In earlier Delphi versions the message box shows here "2", Tokyo - "0". Is this a Tokyo bug or the exceptions should not be handled like this?
Abstract: Examples using try ... except and try .. finally This article provides some information on setting up error handling with Delphi. One of the big advantages of using exceptions is that you do not have to check the result of every function call that you make.
The next question may be what to do with the exception once you catch it. Sometimes, you may want to ignore certain exceptions (such as EConvertError), or execute certain code when an exception has been raised. If you don't handle the exception.
From the point in the code at which it is raised, the exception is passed to its calling code, and so on. Ultimately, if no part of your code handles the exception, the VCL handles it, by displaying a standard error message and then trying to continue the program by handling the next system message or user request.
One of the big advantages of using exceptions is that you do not have to check the result of every function call that you make. Typical C programming involves making a function call (such as allocmem) and checking the result to see if the function succeeded or not.
The Tokyo behaviour is correct. A function that raises an exception does not return a value. You have hitherto been relying on implementation detail.
Consider this code:
Result:=FuncTest;
This executes as follows:
FuncTest
is called.Result
is assigned.Now, because step 1 raises an exception, step 2 does not execute.
If anything, I would say that the behaviour you report from earlier versions is dubious. In this function:
function Test:integer;
begin
Result:=0;
try
Result:=FuncTest;
finally
ShowMessage(Result.ToString);
end;
end;
The statement Result:=FuncTest
raises an exception and so Result
should not be modified by that statement. Another way to think of it is that the function is called but the assignment is not executed.
One of the problems with the Delphi ABI is that function return values are sometimes implemented as implicit var
parameters. Which means that the assignment may or may not happen. To demonstrate:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
TRec1 = record
X1: NativeInt;
end;
TRec2 = record
X1: NativeInt;
X2: NativeInt;
end;
function GetRec1: TRec1;
begin
Result.X1 := 1;
raise Exception.Create('');
end;
function GetRec2: TRec2;
begin
Result.X1 := 1;
raise Exception.Create('');
end;
procedure Main;
var
Rec1: TRec1;
Rec2: TRec2;
begin
Rec1 := Default(TRec1);
Writeln(Rec1.X1);
try
Rec1 := GetRec1;
except
end;
Writeln(Rec1.X1);
Rec2 := Default(TRec2);
Writeln(Rec2.X1);
try
Rec2 := GetRec2;
except
end;
Writeln(Rec2.X1);
end;
begin
Main;
Readln;
end.
This outputs:
0 0 0 1
Which is rather disappointing. It should not be possible to modify the caller's variable, but the use of an implicit var
parameter rather than a value return allows this leakage. In my view this is a serious flaw in the design of the Delphi ABI, a flaw that you will not find in most other languages.
In your code, there is no var
parameter because the return type is transferred in a register. In which case any Delphi version that outputs 2
is broken.
Fundamentally though, your code is mistaken in its expectations. If a function raises an exception then you must assume that the return value is ill-defined.
Finally, your code outputs 0
in XE3 and XE7, so I wonder how far back you need to go to see a value of 2
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With