Autor Beitrag
JayEff
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2971

Windows Vista Ultimate
D7 Enterprise
BeitragVerfasst: Di 09.08.05 22:34 
Hi Leute. ich hab einen Record wie folgt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Type
    TServer = Record
        IP: String;
        Name: String;
        Info: String;
        user: String;
        pw: String;
    End;

Und einen dyn. Array:
ausblenden Delphi-Quelltext
1:
2:
3:
Var
    Form1: TForm1;
    ServerList: Array Of TServer;

Und in der folgenden Prozedur versuche ich, einen Eintrag zu löschen (bisher nur, alle Einträge nach oben zu schieben :roll: )
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
Procedure TForm1.Button5Click(Sender: TObject);
Var
    schritte, removed, i: Integer;
    temp:TServer;
Begin
    If ListBox1.ItemIndex >= 0 Then
    Begin
        schritte := length(ServerList) - ListBox1.ItemIndex;
        removed := ListBox1.ItemIndex;
        For i := 1 To schritte Do
        Begin
          temp.name:=serverList[removed + i].name;
          temp.info:=serverList[removed + i].info;
          temp.ip:=serverList[removed + i].ip;   <<--Accesviolation?! warum grade hier?? oO
          temp.user:=serverList[removed + i].user;
          temp.pw:=serverList[removed + i].pw;

          ServerList[removed+i-1].Name:=temp.name;
          ServerList[removed+i-1].info:=temp.info;
          ServerList[removed+i-1].ip:=temp.ip;
          ServerList[removed+i-1].user:=temp.user;
          ServerList[removed+i-1].pw:=temp.pw;
        End;
    End
    Else
        Messagedlg('Kein Server ausgewählt!', mtError, [mbOK], 0);
End;

Bölderweise erhalte ich immer beim letzten Schleifendurchlauf eine Zugriffsverletzung.
Gefüllt ist der Array so:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
ini-file:
[ServerList]
Count=4
IPs=456.456.456.456,5,5,5
Names=456,5,5,5
Infos='546789456789456789,5,5,5'
users=456,5,5,5
pws=879,5,5,5
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
Var
    IPs, Names, Infos, pws, users: TStringList;
    i: Integer;
Begin
    IPs := TStringList.Create;
    Names := TStringList.Create;
    Infos := TStringList.Create;
    pws := TStringList.Create;
    users := TStringList.Create;
    SetLength(ServerList, ini.ReadInteger('ServerList''count'0));
    Names.CommaText := ini.ReadString('ServerList''names''');
    IPs.CommaText := ini.ReadString('ServerList''IPs''');
    Infos.CommaText := ini.ReadString('ServerList''Infos''');
    pws.CommaText := ini.ReadString('ServerList''pws''');
    users.CommaText := ini.ReadString('ServerList''users''');
    ListBox1.Items.Assign(Names);
    For i := 0 To length(ServerList) - 1 Do
    Begin
        ServerList[i].Name := Names[i];
        ServerList[i].IP := IPs[i];
        ServerList[i].Info := Infos[i];
        ServerList[i].user := users[i];
        ServerList[i].pw := pws[i];
    End;
    IPs.Free;
    Names.Free;
    Infos.Free;
    pws.Free;
    users.Free;

Wieso? Ich versteh es nicht!
:?!?:
Bitte Helft mir!

_________________
>+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Di 09.08.05 23:01 
Button5Click:

Nehmen wir mal an, ListBox1.ItemIndex = 0. Damit wäre schritte = length(ServerList) und removed = 0. Die Schleifenvariable i bewegt sich also im Bereich von 1 bis length(ServerList) ---> serverList[removed + i] enspricht im letzten Schleifendurchlauf serverList[length(ServerList)] und überschreitet somit ganz klar die obere Arraygrenze. serverList[length(ServerList) - 1] ist das letzte Arrayelement.

Wenn du die Listeneinträge vom ausgewählten an abwärts einfach nur "hochschieben" willst, brauchst du doch auch gar kein "temp". Ich würde das ungefähr so machen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
        for i := ListBox1.ItemIndex to High(ServerList) - 1 do
        begin
          ServerList[i].Name := ServerList[i + 1].Name;
          ServerList[i].info := ServerList[i + 1].info;
          ServerList[i].ip := ServerList[i + 1].ip
          ServerList[i].user := ServerList[i + 1].user
          ServerList[i].pw := ServerList[i + 1].pw;
        end;

Danach sollte die Länge der Liste natürlich noch um 1 reduziert werden, sonst gibt's den letzten Eintrag doppelt.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 09.08.05 23:20 
Wenn Du nicht grad .NET-kompatiblen Source voraussetzt, kannst Du das recht schnell mit Move machen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
I := LB.ItemIndex;

If I = -1 Then 
    Exit;

Finalize(ServerList[I]);
Move(ServerList[I], ServerList[I + 1], SizeOf(TServerEintrag) * (High(ServerList) - I));


ACHTUNG: Bin mir bei Move wegen Src und Dest nicht ganz sicher, müsste aber eigentlich so stimmen, ansonsten 1.<-->2. Param. Das Finalize gibt den Speicher der bisherigen Strings frei. Konnt's aber leider grad nicht testen :(

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
JayEff Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2971

Windows Vista Ultimate
D7 Enterprise
BeitragVerfasst: Di 09.08.05 23:37 
Finalize? Move? HÄ? Also
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
        i := ListBox1.ItemIndex;
        Finalize(ServerList[i]);
        Move(ServerList[i], ServerList[i + 1], SizeOf(TServer) * (High(ServerList) - i));
        SetLength(ServerList, length(ServerList) - 1);
        ListBox1.DeleteSelected;

Klappt schonmal nicht, ich tausche mal die param1 und 2...

edit1:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
        i := ListBox1.ItemIndex;
        Finalize(ServerList[i]);
        Move(ServerList[i + 1], ServerList[i], SizeOf(TServer) * (High(ServerList) - i));
        //SetLength(ServerList, length(ServerList) - 1);
        ListBox1.DeleteSelected;

brachte keine besserung, nun, das löschen klappt, auch ohne die auskommentierte zeile, versuche ich dann aber aufs letzte element zuzugreifen, t gibts eine zugriffsverletzung.
Ich kommentierte die Zeile aus => Alles klappt, bis ich einen neuen eintrag hinzufüge und dann versuche, auf diesen zuzugreifen.
ausblenden hinzufügen:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
Var
    Name, IP, user, pw, Infos: String;
Begin
    Name := InputBox('Name''Name oder Kennzeichen des Servers''');
    IP := InputBox('IP''IP des Servers (Wird nicht auf Gültigkeit geprüft!)''');
    Infos := InputBox('Infos''Infos über den Server, z.B. Rates (optional)''');
    user := InputBox('Login ID''Ihre Login ID auf diesem Server''');
    pw := InputBox('Passwort''Ihr Passwort auf diesem Server''');
    If (length(Name) > 0And
        (length(IP) > 0And
        (length(user) > 0And
        (length(pw) > 0Then
    Begin
        showmessage(IntToStr(length(ServerList)));
        SetLength(ServerList, length(ServerList) + 1);
        showmessage(IntToStr(length(ServerList)));
        ServerList[length(ServerList) - 1].Name := Name;
        ServerList[length(ServerList) - 1].IP := IP;
        ServerList[length(ServerList) - 1].Info := Infos;
        ServerList[length(ServerList) - 1].user := user;
        ServerList[length(ServerList) - 1].pw := pw;
        ListBox1.Items.add(Name);
    End;
End;

_________________
>+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.


Zuletzt bearbeitet von JayEff am Di 09.08.05 23:46, insgesamt 1-mal bearbeitet
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Di 09.08.05 23:41 
user profile iconBenBE hat folgendes geschrieben:
Wenn Du nicht grad .NET-kompatiblen Source voraussetzt, kannst Du das recht schnell mit Move machen:

Ja, schnell und gefährlich, denn das verursacht Speicherlecks! Wenn die ganzen String-Felder des ersten Records per Move überschrieben werden, bekommt das der Delphicompiler nicht mit und wird so auch keinen Code generieren, der den Referenzzähler der Strings dekrementiert und ggf. den durch die Strings belegten Speicher freigibt!

Edit: Ups, habe das Finalize übersehen. :oops:

Moment, bleibt trotzdem gefährlich. Mit Finalize kann man die Strings des ersten Records (derjenige, der gelöscht/überschrieben wird) freigeben. Was ist aber mit denjenigen des letzten Records? Ich hoffe folgendes Beispiel verdeutlicht, was ich meine:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
var
  s: array of string;

procedure Init;
var
  i: Integer;
begin
  SetLength(s, 10);
  for i := 0 to High(s) do
    s[i] := Format('String%d', [i]);
end;

procedure TestProc;
begin
  // string in erstem Arrayelement freigeben, weil es gleich überschrieben wird
  Finalize(s[0]);
  Move(s[1], s[0], High(s) * SizeOf(s[0]));
  // s[8] und s[9] zeigen jetzt auf denselben string, 'String9'.
  // Dieser sollte also einen Refcount von 2 haben, hat aber nur 1!

  // array um 1 verkürzen:
  SetLength(s, Length(s) - 1);
  // s[9] fliegt damit aus dem array, und da sein Refcount = 1 war, wird der
  // Speicher von 'String9' freigegeben ==> s[8] zeigt auf diesen freigegeben Speicher,
  // was früher oder später zu Fehlern führen wird
end;
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mi 10.08.05 00:10 
Hab nochmal nachgetestet (bei mir funzen folgende beiden Varianten (D7 Ent):

ausblenden Variante 1:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.Button1Click(Sender: TObject);
var
    I: Integer;
begin
    I := 0;

    If I = -1 Then
        Exit;

    Finalize(ServerList[I]);
    Move(ServerList[I + 1], ServerList[I], SizeOf(TStringRecord) * (High(ServerList) - I));
    SetLength(ServerList, High(ServerList));
end;


ausblenden Variante 2:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure TForm1.Button1Click(Sender: TObject);
var
    I: Integer;
    TmpEntry: TStringRecord;
begin
    I := 0;

    If I = -1 Then
        Exit;

    TmpEntry := ServerList[I];
    Move(ServerList[I + 1], ServerList[I], SizeOf(TStringRecord) * (High(ServerList) - I));
    ServerList[High(ServerList)] := TmpEntry;
    SetLength(ServerList, High(ServerList));
end;


Mein TestSource sieht so aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
type 
    TStringRecord = record
        S1, S2, S3, S4, S5: String;
    end;

var
    ServerList: Array of TStringRecord;


ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.FormCreate(Sender: TObject);
begin
    SetLength(ServerList, 100);
    ServerList[0].S3 := 'Test';    
    ServerList[99].S2 := Caption;    
    Button1Click(nil);
    Caption := ServerList[98].S2;
    SetLength(ServerList, 0);
end;


Das Programm ließ sich bei mir fehlerfrei beenden und brachte keinerlei Exceptions.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Mi 10.08.05 00:12 
user profile iconJayEff hat folgendes geschrieben:
brachte keine besserung, nun, das löschen klappt, auch ohne die auskommentierte zeile, versuche ich dann aber aufs letzte element zuzugreifen, t gibts eine zugriffsverletzung.
Ich kommentierte die Zeile aus => Alles klappt, bis ich einen neuen eintrag hinzufüge und dann versuche, auf diesen zuzugreifen.


Liegt wahrscheinlich genau an dem Effekt, den ich oben beschrieben habe.
Hast du's mal auf meine Weise probiert, also "ServerList[i].Name := ServerList[i + 1].Name;" usw.?
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mi 10.08.05 00:23 
@SMD: Man kann auch ServerList[i] := ServerList[i+1]; schreiben. Delphi kopiert dann automatisch gleich alle Einträge des Records korrekt.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Mi 10.08.05 00:33 
*handgegenkopfschlag* Na klar, du hast recht, ist viel einfacher so.

Deine zwei Varianten oben sind allerdings trotzdem beide "falsch", da potenziell gefährlich. Verstehst du, was ich meine?
Falls nicht, starte mal ein neues Delphiprojekt, füge meinen Testcode von oben ein und zusätzlich einen Button mit folgendem Handler:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Init;
  TestProc;
  ShowMessage(s[High(s)]);
end;


Bei mir verursacht ShowMessage eine Exception, und das ist auch richtig so, denn es wird versucht ein string anzuzeigen, dessen Speicher bereits freigegeben wurde. Das Problem entsteht dadurch, dass nach einer solchen Move-Operation, wie wir sie hier haben, das letzte und vorletzte Element beide dieselben strings referenzieren, diese strings jedoch weiterhin nur einen Refcount von 1 haben, obwohl es jetzt 2 sein müsste.

Dieses Problem kann folgendermaßen gelöst werden:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
    Finalize(ServerList[I]);
    Move(ServerList[I + 1], ServerList[I], SizeOf(TStringRecord) * (High(ServerList) - I));
    FillChar(ServerList[High(ServerList)], SizeOf(TStringRecord), 0);
    SetLength(ServerList, High(ServerList));
JayEff Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2971

Windows Vista Ultimate
D7 Enterprise
BeitragVerfasst: Mi 10.08.05 15:39 
Yaay! Bisher keine Probleme! Sollten doch wieder Probleme auftreten, melde ich mich, ansonsten: DANKE! :)

_________________
>+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.