2016-12-08 31 views
8

natknąłem się na błąd w Delphi 10 Seattle Aktualizacja 1. Weźmy następujący kod:Jak obejść błąd Delphi 10 z TList <_AnyDynamicArrays_>?

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; 

na pierwszy rzut oka wydaje się, że TList<T> nie właściwie zarządzać żywotność dynamicznych tablic zawiera, ale z drugiej strony, działa dobrze, jeśli skompilowany w 64 bitach, tylko zawiesza się w 32 bitach (Rozumiem, że to nie znaczy, że błąd nie występuje w 64 bitach ...).

Należy pamiętać, że SplitString został użyty, ponieważ jeśli była pierwszą funkcją zwracającą dynamiczną tablicę, która przyszła mi do głowy. Pierwotny problem napotkano na TList<TBookmark>, który wykazuje ten sam problem.

Jest możliwość obejścia błędu Procedura przepisywania Button1Click tak:

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; 

Ale dzieje wokół wszystkie moje aplikacje modyfikując je w celu obejścia tego błędu to naprawdę nie jest mój preferowany opcję. Wolałbym znaleźć szkodliwą rutynę i załatwić ją w pamięci, jeśli to możliwe.

Jeśli ktoś napotkał ten problem i znalazł obejście, byłbym wdzięczny. W przeciwnym razie, opublikuję moje, gdy/jeśli znajdę odpowiednie obejście.

Proszę również o komentarz, jeśli problem nadal występuje w Berlinie.

+0

sam błąd w Berlinie Upd2. –

+1

Od zgłoszonych błędów z typowym 'TList', wydaje się, że jest to pole minowe. –

Odpowiedz

8

W końcu błąd nadal występował w 64 bitach. To się nie zawiesiło dla TStringDynArray, ale dotyczyło to innych typów dynamicznych tablic.

Źródłem problemu można znaleźć w poniższym kodzie w 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; 

Co się stało to, że źle TypeInfo jest przekazywana do DynArrayClear. W przypadku numeru TList<TStringDynArray> zamiast TypeInfo(TStringDynArray) jest przekazywany TypeInfo(TArray<TStringDynArray>). Z tego co wiem, prawidłowe wywołanie to:

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

Procedura, która jest prywatna, utrudnia przechwytywanie. Zrobiłem to wykorzystując fakt, że rekordzista może nadal mieć dostęp do prywatnej sekcji płyt w Delphi 10. Sądzę, że będzie to bardziej skomplikowane dla użytkowników Berlina.

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

Mamy nadzieję, że będzie to naprawić Embarcadero kiedyś ...

+0

'pDynArrayTypeInfo (NativeInt (FTypeInfo) + pDynArrayTypeInfo (FTypeInfo) .Name) .elType ^' jest tym, co otrzymasz, jeśli użyjesz 'ElType'. –

+5

Zgłoszono: [RSP-16511: Crash w TListHelper.DoSetItemDynArray() z powodu niewłaściwego TypeInfo użytego do wyczyszczenia starej tablicy dynamicznej] (https://quality.embarcadero.com/browse/RSP-16511) –

+1

To prawie tak, jakby nie napisałem testów jednostkowych dla każdego szczególnego przypadku w tym pomocniku ........ –