Delphi 5编译器错误返回接口指针而不是返回值

我向你展示了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

但最后阻止(调用接口方法)是问题。 它吹走了存储在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

在对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,即使你做到了,

“你想让我说什么?”

我知道困难。 我真正需要的是某种解决方法。


正如您观察到的那样,编译器将结果存储到EBX ,但在随后将EBX复制到EAX以将结果返回给调用方之前,将其覆盖。

编译器应该执行以下操作之一:

  • 使用不同的寄存器来临时存储结果值,以便其使用EBX不会破坏结果值,或
  • 在拨打Grob不使用EBX ,或者
  • 将结果值存储在比寄存器更持久的东西上,如堆栈中。
  • 很显然,选项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;
    
    链接地址: http://www.djcxy.com/p/89891.html

    上一篇: Delphi 5 compiler bug returning interface pointer rather than return value

    下一篇: How to get address of Delphi code label in another function?