10

以下代码仅在编译为 64 位时才会在 Delphi 10.3.1 中生成异常 (c0000005 ACCESS_VIOLATION)。

但是,相同的代码在 Delphi 10.3.1 中编译为 32 位时不会产生异常。此外,当编译为 32 位或 64 位时,它在 Delphi 10.2.3 中也不会失败。

program CrashOn64;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyBaseClass = class
  protected
    procedure Setup(aParams: array of const); virtual;
  public
  end;

type
  TMyWorkClass = class(TMyBaseClass)
  protected
    procedure DoSetup; virtual;
  public
    procedure Setup(aParams: array of const); override;
  end;

{ TMyBaseClass }

procedure TMyBaseClass.Setup(aParams: array of const);
begin
end;

{ TMyWorkClass }

procedure TMyWorkClass.DoSetup;
begin
  inherited;   
end;

procedure TMyWorkClass.Setup(aParams: array of const);
begin
  inherited;
  DoSetup
end;

// main

var
  myClass: TMyWorkClass;
begin
  try
    myClass:=TMyWorkClass.Create;
    try
      myClass.Setup([123]); // <-- Crash on Windows 64-bit
      writeln('OK!')
    finally
      myClass.Free
    end
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;

  readln; // Wait for Enter key
end.

问题似乎在于参数类型是array of const. 如果我们更改为 64 位,代码仍然会失败array of constarray of integer因此新的 Delphi 编译器似乎存在参数数量未知的数组的问题。我们找到了通过创建一个类型来避免编译器错误的技巧,array of integer但是这个技巧对于我们需要的东西是不可用的array of const

这是根据CPU 视图在 Delphi 10.3.1 中为 64 位生成的汇编代码:

CrashOn64.dpr.41: inherited;
0000000000428888 488B7528         mov rsi,[rbp+$28]
000000000042888C 488D7D20         lea rdi,[rbp+$20]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,$1fffffffffffffff <<< What????????
000000000042889A F348A5           rep movsq                     <<< Crashes here.
000000000042889D A5               movsd
000000000042889E 66A5             movsw
00000000004288A0 A4               movsb
00000000004288A1 488B4D50         mov rcx,[rbp+$50]
00000000004288A5 488D5520         lea rdx,[rbp+$20]
00000000004288A9 448B4560         mov r8d,[rbp+$60]
00000000004288AD E8CEFEFFFF       call TMyBaseClass.Setup

这是在 Delphi 10.2.3 中为相同功能生成的 64 位代码:

CrashOn64.dpr.41: inherited;
0000000000427329 488B4D50         mov rcx,[rbp+$50]
000000000042732D 488B5528         mov rdx,[rbp+$28]
0000000000427331 448B4560         mov r8d,[rbp+$60]
0000000000427335 E8E6FEFFFF       call TMyBaseClass.Setup

这是 Delphi 10.3.1 中的 64 位编译器错误还是我们遗漏了什么?有什么解决方法吗?

4

2 回答 2

14

这是错误,应该报告1如问题中所述,每种类型的open array都失败。

一种解决方法是const在方法中将数组定义为 a:

procedure Setup(const aParams: array of const); 

将打开的数组声明为const,通过引用传递数组,而没有 const 时,它将作为副本按值传递。在这种情况下,Rio 版本会失败。


1报错:Rio中调用带开放数组参数的继承函数时访问冲突

于 2019-04-15T17:21:04.693 回答
1

这个错误更广泛,它不处理过程/函数的 VARed 短字符串。

consider this SIMPLE code...
procedure Copyit(var s: shortstring); // VAR is important, pass by value ok
begin
   debugform(s); // break point here
end;

procedure TForm1.Button1Click(Sender: TObject);
var
stuff: shortstring;
begin
   stuff:= 'This is a demo string';
   CopyIt(stuff);
end;

在 32 位模式下,它会生成您期望的代码。在 64 位模式下,它生成的代码完全如上所示,一个巨大的内存移动, rcx 设置为 $1FFFFFFFFFFFFFFFF !!!

于 2020-01-24T16:23:09.777 回答