我在Delphi 5编译器中向您展示了一个错误。我知道不会有任何解决方法;但解决方法是超级
program Project1;
uses
Dialogs, SysUtils;
{$R *.RES}
type
IFoo = interface
['{D68DA49A-F870-433D-9343-4964BFECFF27}']
procedure Grob(a: Integer; b: Integer);
end;
TFoo = class(TInterfacedObject, IFoo)
public
procedure Grob(a: Integer; b: Integer); virtual;
end;
procedure TFoo.Grob(a: Integer; b: Integer);
begin
end;
function DoStuff(): Integer;
var
foo: IFoo;
begin
foo := TFoo.Create;
try
Result := 1;
Exit;
finally
foo.Grob(0, 0);
end;
Result := 2;
end;
var
n: Integer;
begin
n := DoStuff;
if n <> 0 then
ShowMessage('Failed: '+IntToStr(n))
else
ShowMessage('Passed: '+IntToStr(n));
end.
真正的胆量是 DoStuff 的功能,应该返回一个:
function DoStuff(): Integer;
var
foo: IFoo;
begin
foo := TFoo.Create;
try
Result := 1;
Exit;
finally
foo.Grob(0, 0);
end;
Result := 2;
end;
该功能应返回一个。相反,它返回接口对象的地址:
代码实际上开始将结果设置为1:
Project1.dpr.30: Result := 1; mov ebx,$00000001 ; place return value 1 in EBX Project1.dpr.31: Exit; call @TryFinallyExit ; call the finally block jmp DoStuff + $6E
并且当函数即将返回时,它会将EBX复制到EAX中以便返回它:
mov eax,ebx ;EBX into EAX for return
但是最后阻塞(调用interfaced方法)就是问题所在。它会消除存储在EBX中的返回值:
We arrive here from the call @TryFinallyExit Project1.dpr.33: foo.Grob(0, 0); xor ecx,ecx xor edx,edx mov eax,[ebp-$04] mov ebx,[eax] <----- overwriting ebx with interface address call dword ptr [ebx+$0c] ret
在“call”到finally块之后,它返回跳转,将其发送到:
Project1.dpr.36: Result := 2; ... xor eax,eax pop edx pop ecx pop ecx mov fs:[eax],edx push $00442e1f lea eax,[ebp-$04] call @IntfClear ret ... mov eax,ebx <----- places overwritten EBX into EAX for return Project1.dpr.37: end; pop ebx pop ecx pop ebp ret
返回值而不是一个或两个,是接口指针的地址。
我知道你们都没有Delphi 5.即使你这样做了,
“你想让我说什么?”
我知道困难。我真正需要的是某种解决方法。
答案 0 :(得分:3)
正如您所观察到的,编译器将结果存储到EBX
中,然后在将EBX
复制到EAX
之前覆盖它,以便将结果返回给调用者。
编译器应该执行以下操作之一:
EBX
不会破坏结果值,或EBX
或Grob
显然,选项1和2并不是您可以使用的,但后者是您需要在此示例中实现的解决方法 - 使用局部变量保存您想要的Result
值,直到您准备好返回它:
function DoStuff(): Integer;
var
foo: IFoo;
MyResult: Integer;
begin
foo := TFoo.Create;
try
try
MyResult := 1;
Exit;
finally
foo.Grob(0, 0);
end;
MyResult := 2;
finally
Result := MyResult;
end;
end;