我偶然发现了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<T>
没有正确管理它包含的动态数组的生命周期,但是再次,如果以64位编译它就可以正常工作,它只会崩溃32位(我知道它并不意味着这个错误不会出现在64位......)。
请注意,使用了SplitString,因为if是第一个返回动态数组的函数。 TList<TBookmark>
遇到了同样问题的原始问题。
可以解决错误重写过程的问题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;
但是绕过我修改它们以解决这个bug的所有应用程序并不是我的首选选项。我更喜欢找到有问题的例程并在可能的情况下将其修补在内存中。
如果有人遇到这个问题并找到了解决方法,我将不胜感激。否则,如果我找到合适的解决方法,我会发布我的。
此外,如果问题仍然存在于柏林,请发表评论。
答案 0 :(得分:8)
毕竟,这个bug仍然存在于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<TStringDynArray>
,则会传递TypeInfo(TArray<TStringDynArray>)
而不是TypeInfo(TStringDynArray)
。据我所知,正确的电话是:
DynArrayClear(OldItem, pDynArrayTypeInfo(NativeInt(FTypeInfo) + pDynArrayTypeInfo(FTypeInfo).Name).elType^);
私有程序使拦截变得复杂。我这样做是因为记录助手仍然可以访问Delphi 10中记录的私有部分。我想这对柏林用户来说会更复杂。
function TMyHelper.GetDoSetItemDynArrayAddr: TDoSetItemDynArrayProc;
begin
Result := Self.DoSetItemDynArray;
end;
希望Embarcadero有一天能解决这个问题......