Как обойти ошибку 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 когда-нибудь это исправит...