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.