Как обойти ошибку Delphi 10 с помощью TList?
я наткнулся на ошибку в 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;
но обойти все мои приложения, изменив их, чтобы обойти эту ошибку, на самом деле не будет моим предпочтительным вариантом. Я бы предпочел найти оскорбительную рутину и по возможности вставить ее в память.
если кто сталкивался с этой проблемой и нашел обходной путь, буду благодарен. В противном случае я отправлю свой, когда/если я найду правильный обходной путь.
кроме того, пожалуйста, прокомментируйте, если проблема все еще присутствует в Берлине.
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. В случае a 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 когда-нибудь это исправит...