| Autor |
Beitrag |
JayEff
      
Beiträge: 2971
Windows Vista Ultimate
D7 Enterprise
|
Verfasst: Di 09.08.05 22:34
Hi Leute. ich hab einen Record wie folgt:
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:
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  )
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:
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 | 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
      
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: 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:
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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Di 09.08.05 23:20
Wenn Du nicht grad .NET-kompatiblen Source voraussetzt, kannst Du das recht schnell mit Move machen:
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 
      
Beiträge: 2971
Windows Vista Ultimate
D7 Enterprise
|
Verfasst: Di 09.08.05 23:37
Finalize? Move? HÄ? Also
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:
Delphi-Quelltext 1: 2: 3: 4: 5:
| i := ListBox1.ItemIndex; Finalize(ServerList[i]); Move(ServerList[i + 1], ServerList[i], SizeOf(TServer) * (High(ServerList) - i)); 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. 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) > 0) And (length(IP) > 0) And (length(user) > 0) And (length(pw) > 0) Then 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
      
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: Di 09.08.05 23:41
BenBE 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.
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:
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 Finalize(s[0]); Move(s[1], s[0], High(s) * SizeOf(s[0])); SetLength(s, Length(s) - 1); end; |
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 10.08.05 00:10
Hab nochmal nachgetestet (bei mir funzen folgende beiden Varianten (D7 Ent):
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; |
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:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| type TStringRecord = record S1, S2, S3, S4, S5: String; end;
var ServerList: Array of TStringRecord; |
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
      
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: Mi 10.08.05 00:12
JayEff 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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: 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
      
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: 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:
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:
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 
      
Beiträge: 2971
Windows Vista Ultimate
D7 Enterprise
|
Verfasst: Mi 10.08.05 15:39
Yaay! Bisher keine Probleme! Sollten doch wieder Probleme auftreten, melde ich mich, ansonsten: DANKE! 
_________________ >+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.
|
|
|