Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Library: Object-Pascal / Delphi-Language (https://www.delphipraxis.net/35-library-object-pascal-delphi-language/)
-   -   Delphi [Win32] Eintrag aus dynamischen Array löschen (https://www.delphipraxis.net/35476-%5Bwin32%5D-eintrag-aus-dynamischen-array-loeschen.html)

jim_raynor 7. Dez 2004 19:08


[Win32] Eintrag aus dynamischen Array löschen
 
Sicher kennen viele dass Problem, einen Eintrag aus einem dynamischen Array zu löschen. Delphi stellt keine vorgefertigte Funktion zur Verfügung, so dass man jedes mal eine eigene Löschroutine schreiben muss. Auch mich hat die Sache genervt und ich habe mich rangesetzt und eine Funktion geschrieben, die genau dies kann.

Delphi-Quellcode:
procedure DeleteArray(const ArrayPtr: Pointer; TypInfo: PTypeInfo; Index: Integer);
var
  NewElem: Integer;
  ElemSize: Integer;
  ElemAddr: Integer;
  ElemCount: Integer;
begin
  Assert(TypInfo.Kind=tkDynArray,'Es werden nur Dynamische Arrays unterstützt');
  // Elementgröße ermitteln
  ElemSize:=PInteger(PByte(Integer(TypInfo)+1)^+Integer(TypInfo)+2)^;
  // Elementanzahl ermitteln
  ElemCount:=PInteger(Integer(ArrayPtr^)-4)^;

  ElemAddr:=Integer(ArrayPtr^)+(Index*ElemSize);

  // Eintrag freigeben

  // Prüfen ob ein Finalize notwendig ist, dazu muss in der Type
  asm
    pushad
    mov esi,TypInfo
    xor eax,eax
    mov al,[esi+$01]
    add esi,eax
    add esi,$06

    mov eax,[esi]
    test eax,eax
    jz @end

    mov edx,[eax]
    mov eax,ElemAddr
    call system.@finalize

  @end:
    popad
  end;
  MoveMemory(Pointer(ElemAddr),Pointer(ElemAddr+ElemSize),ElemSize*((ElemCount-1)-index));
  NewElem:=ElemCount-1;
  // Letztes Element mit nullen füllen
  ZeroMemory(Pointer(Integer(ArrayPtr^)+(NewElem*ElemSize)),ElemSize);

  // Array verkleinern
  asm
    mov ebx,NewElem
    push ebx
    mov ecx,$00000001

    mov edx,TypInfo

    mov eax,ArrayPtr
    call system.@DynArraySetLength
    add esp,$04
  end;
end;
Ich will nicht behaupten, dass diese Funktion in allen Delphi-Versionen funktioniert, da direkt auf Typinformationen und Compilerinterne Funktionen zugegriffen wird, die sich von Version zu Version ändern können. Jedenfalls wurde die Funktion für D5, D6 und D7 erfolgreich getestet. Leider musste ich ein paar schmutzige Asm-Tricks einbauen (die ich nicht mehr erklären kann) da der Compiler mit einigen Registern gemacht hat, was er wollte. Und leider weiss ich auch nicht mehr, was alles im einzelnen bedeutet. Die Funktion habe ich schon vor längerer Zeit erstellt.

Vieles hab ich rausgefunden, in dem ich einfach SetLength irgendwo gemacht habe und mir den asm Output dazu angeschaut habe. Wie man sieht benutze ich auch Finalize um zum Beispiel bei Records enthaltene Strings freizugeben. Objekte müssen aber natürlich wie gewohnt selber freigegeben werden.

Nun gut jetzt noch ein paar Worte zur Benutzung. Wichtig ist dass das Array als eigener Typ definiert wird.
Delphi-Quellcode:
type
  TArrayType = Array of String;
Der Aufruf erfolgt dann relativ einfach:

Delphi-Quellcode:
var
  Arr : TArrayType;
begin
  SetLength(Arr,100);
  Arr[10]:='Test';
  DeleteArray(Addr(Arr),TypeInfo(TArrayType),10);
Parameter 1 gibt die Adresse der Array-Variablen an. Paramater 2 die Typeninformation des Arrays. Erst dadurch bekomme ich Elementgrösse und benötigte Finalisierungsprozesse raus. Paramater 3 gibt schliessen den zu löschenden Eintrag an.

So hoffe das diese Informationen hilfreich sind.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:15 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz