【问题标题】:How to work around Delphi 10's bug with TList<_AnyDynamicArrays_>?如何使用 TList<_AnyDynamicArrays_> 解决 Delphi 10 的错误?
【发布时间】:2017-04-24 04:20:17
【问题描述】:

我在 Delphi 10 Seattle Update 1 中偶然发现了一个错误。让我们使用以下代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
//----------We crash here----------------
  FList.Items[0] := SplitString('H:E', ':');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FList := TList<TStringDynArray>.Create;
  FList.Add(SplitString('H:E', ':'));
  FList.Items[0] := SplitString('H:E', ':');
end;

乍一看,TList&lt;T&gt; 似乎没有正确管理它包含的动态数组的生命周期,但话又说回来,如果以 64 位编译它就可以正常工作,它只会在 32 位崩溃(我理解这并不意味着该错误不存在于 64 位...)。

请注意,使用 SplitString 是因为 if 是我想到的第一个返回动态数组的函数。最初的问题是在 TList&lt;TBookmark&gt; 遇到的,它表现出同样的问题。

可以像这样解决重写过程 Button1Click 的错误:

procedure TForm1.Button1Click(Sender: TObject);
var MyArray : TStringDynArray;
begin
  MyArray := FList.Items[0];
  FList.Items[0] := SplitString('H:E', ':');
  //----------Yeah! We don't crash anymore!-----------
end;

但是绕过我所有的应用程序来修改它们以解决这个错误并不是我的首选。如果可能的话,我更愿意找到有问题的例程并在内存中修补它。

如果有人遇到此问题并找到解决方法,我将不胜感激。否则,当/如果我找到合适的解决方法时,我会发布我的。

另外,如果问题在柏林仍然存在,请发表评论。

【问题讨论】:

  • 柏林 Upd2 出现同样的错误。
  • 从报告的带有通用TList 的错误来看,这似乎是一个雷区。

标签: delphi delphi-10-seattle


【解决方案1】:

毕竟,64 位的错误仍然存​​在。 TStringDynArray 没有崩溃,但其他动态数组类型却崩溃了。

问题的根源在Generics.Collections中的以下代码中:

procedure TListHelper.DoSetItemDynArray(const Value; AIndex: Integer);
type
  PBytes = ^TBytes;
var
  OldItem: Pointer;
begin
  OldItem := nil;
  try
    CheckItemRangeInline(AIndex);

    TBytes(OldItem) := PBytes(FItems^)[AIndex];
    PBytes(FItems^)[AIndex] := TBytes(Value);

    FNotify(OldItem, cnRemoved);
    FNotify(Value, cnAdded);
  finally
    DynArrayClear(OldItem, FTypeInfo); //Bug is here.
  end;
end;

发生的情况是,错误的 TypeInfo 被传递给了 DynArrayClear。对于TList&lt;TStringDynArray&gt;,传递TypeInfo(TArray&lt;TStringDynArray&gt;) 而不是TypeInfo(TStringDynArray)。据我所知,正确的调用是:

DynArrayClear(OldItem, pDynArrayTypeInfo(NativeInt(FTypeInfo) + pDynArrayTypeInfo(FTypeInfo).Name).elType^);

私有过程使得拦截变得复杂。我这样做是因为记录助手仍然可以访问 Delphi 10 中记录的私有部分。我想这对柏林的用户来说会更复杂。

function TMyHelper.GetDoSetItemDynArrayAddr: TDoSetItemDynArrayProc;
begin
  Result := Self.DoSetItemDynArray;
end;

希望 Embarcadero 有朝一日会修复它...

【讨论】:

  • pDynArrayTypeInfo(NativeInt(FTypeInfo) + pDynArrayTypeInfo(FTypeInfo).Name).elType^ 是你使用ElType 得到的结果。
  • 这几乎就像他们没有为这个助手中的每个特殊情况编写单元测试......
  • @LURD 如果你的意思是我可以使用pDynArrayTypeInfo(FTypeInfo).elType^,不,我不能。虽然pDynArrayTypeInfo(FTypeInfo).Name 被定义为一个字节,但它被实现为一个短字符串,正如_DynArrayClear 中的代码所证明的那样。
  • ElType 是 TListHelper 中的一个属性。 getter 定义为:function TListHelper.GetElType: Pointer; begin Result := PDynArrayTypeInfo(PByte(FTypeInfo) + PDynArrayTypeInfo(FTypeInfo).name).elType^; end; 只需使用DynArrayClear(OldItem,ElType);
猜你喜欢
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多