Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - AccessViolation / Zugriffsverletzung


Jagg - Sa 06.12.08 05:15
Titel: AccessViolation / Zugriffsverletzung
Hallo zusammen,

ich hoffe, bei Euch Hilfe zu finden :
Ich habe diesen Code, wo dynamische TRadiobuttons erstellt werden...

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:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
procedure TfmDynArt.FormShow(Sender: TObject);
 var iLauf_FormProp : integer;
begin
  Label_artnrWert.Caption        := ArtNr;
  Label_BezeichnungWert.Caption  := NameCopy+'...';
  fmDynArt.Caption               := ' Artikel-Zusatz (ESC = Abbruch)';
  Label_BezeichnungWert.Width    := Length(NameCopy)*8-15;
  //fmDynArt.Width                 := Length(Label_bezeichnung.Caption+NameCopy)*8;
  iDyn_Zusatz                    := 1;
  iDynLauf_Top                   := 25;
  iDynLauf_ZusatzEND             := 0;
  fmMain.Table_speisenGetraenke.First;
  while not fmMain.Table_speisenGetraenke.Eof do
  begin
    if pos(ArtNr+',',fmMain.Table_speisenGetraenke.FieldByName('Name').AsString)<>0 then
    begin
      Pos_3Striche  := pos('---',fmMain.Table_speisenGetraenke.FieldByName('Name').AsString);
      Dyn_Zusatz[iDyn_Zusatz]            := TRadioButton.Create(fmDynArt);
      Dyn_Zusatz[iDyn_Zusatz].Parent     := fmDynArt;
      Dyn_Zusatz[iDyn_Zusatz].Left       := Label_BezeichnungWert.Left;
      Dyn_Zusatz[iDyn_Zusatz].Top        := Label_BezeichnungWert.Top+iDynLauf_Top;
      Dyn_Zusatz[iDyn_Zusatz].Font.Name  := 'Tahoma';
      Dyn_Zusatz[iDyn_Zusatz].Font.Size  := 10;
      Dyn_Zusatz[iDyn_Zusatz].Font.Style := [fsbold];
      Dyn_Zusatz[iDyn_Zusatz].OnKeyPress := Dyn_Zusatz_KeyPress;
      RadioButtonTextNEU := Copy(fmMain.Table_speisenGetraenke.FieldByName('Name').AsString,1,Pos_3Striche-1);
      Pos_ArtZusatz := pos(';',fmMain.Table_speisenGetraenke.FieldByName('Name').AsString);
      Pos_Komma := pos(',',fmMain.Table_speisenGetraenke.FieldByName('Preis').AsString);
      case Pos_Komma of
        0 : begin
              case Length(fmMain.Table_speisenGetraenke.FieldByName('Preis').AsString) of
                1 : Dyn_MaskEdit.EditMask := '0,99;1;0';   // Wenn Länge Einzelpreis = 1, z.b. 7  €...ArtNr : 36
                2 : Dyn_MaskEdit.EditMask := '00,99;1;0';  // Wenn Länge Einzelpreis = 2, z.b. 13 €...ArtNr : 23
              end;
            end;
        2 : Dyn_MaskEdit.EditMask := '0,99;1;0';  // Wenn Kommma an 2 Stelle, z.b. 7,5  €...ArtNr : 45
        3 : Dyn_MaskEdit.EditMask := '00,99;1;0'; // Wenn Kommma an 3 Stelle, z.b. 11,5 €...ArtNr : 47
      end;
      Dyn_MaskEdit.Text := fmMain.Table_speisenGetraenke.FieldByName('Preis').AsString;
      if ArtNr[1] <> 'm' then
        Dyn_Zusatz[iDyn_Zusatz].Caption := Copy(RadioButtonTextNEU,Pos_ArtZusatz+1,Length(RadioButtonTextNEU))+
                                           '('+Dyn_MaskEdit.EditText+' €'+')'
      else
        Dyn_Zusatz[iDyn_Zusatz].Caption := Copy(RadioButtonTextNEU,Pos_ArtZusatz+1,Length(RadioButtonTextNEU));
      Dyn_Zusatz[iDyn_Zusatz].Width := Length(Dyn_Zusatz[iDyn_Zusatz].Caption)*8+15;
      inc(iDyn_Zusatz);
      inc(iDynLauf_ZusatzEND);
      iDynLauf_Top := iDynLauf_Top + 25;
      fmMain.Table_speisenGetraenke.Next;
    end
    else
      fmMain.Table_speisenGetraenke.Next;
  end;
  Dyn_Zusatz[1].Checked := True;


...zuvor habe ich die TRadiobutton so deklariert :

Delphi-Quelltext
1:
var Dyn_Zusatz    : array [1..20] of TRadioButton;                    


...nun ist es so,das 6 TRadionbuttons erstellt werden
und dementsprechend werden diese auch wieder freigegeben :

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TfmDynArt.FormClose(Sender: TObject; var Action: TCloseAction);
 var iDynLauf_ZusatzBEGIN : integer;
begin  
  for iDynLauf_ZusatzBEGIN := 1 to iDynLauf_ZusatzEND do
      Dyn_Zusatz[iDynLauf_ZusatzBEGIN].Free;
end;


... ich habe auch nachgeprüft, es werden WIRKLICH nur 6 TRadiobuttons erstellt und wieder freigegeben.
In diesem Fall werden nur 6 erstellt,weil abhängig von der Artikelnummer (ArtNr) es demzufolgend verschiedene Ausprägungen gibt.

NUN kommt der Code, wo ich vermute das der Fehler auftritt.
Es handelt sich hier bei um ein Ereignis KeyPress : Dyn_Zusatz[iDyn_Zusatz].OnKeyPress := Dyn_Zusatz_KeyPress;

...es wurde vorher so deklariert : type procedure Dyn_Zusatz_KeyPress(Sender: TObject; var Key: Char);

Code mit der Vermutung auf Fehler "Access Violation / Zugriffsverletzung :

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TfmDynArt.Dyn_Zusatz_KeyPress(Sender: TObject; var Key: Char);
begin
  if key = #13 then
  begin
    Pos_PreisBEGIN := pos('(',(Sender as TRadiobutton).Caption);
    Pos_PreisEND   := pos(')',(Sender as TRadiobutton).Caption);
    PreisCopy      := Copy((Sender as TRadiobutton).Caption,Pos_PreisBEGIN+1,Pos_PreisEND-1-Pos_PreisBEGIN);
    Pos_ZusatzEND  := pos('(',(Sender as TRadiobutton).Caption);
    ZusatzCopy     := Copy((Sender as TRadiobutton).Caption,1,Pos_ZusatzEND-1);
    fmMain.Stringgrid_artikel.Cells[1,fmMain.Stringgrid_artikel.RowCount-1] := NameCopy+ZusatzCopy;
    fmMain.Stringgrid_artikel.Cells[4,fmMain.Stringgrid_artikel.RowCount-1] := PreisCopy;
    fmDynArt.Close;
  end;
end;


...NUN erkläre Ich euch den Ablauf :
Wenn bei Eingabe der ArtNr, diese verschiedene Ausprägungen hat, erscheint ein neues Fenster, wo die dyn. TRadiobuttons aufgelistet werden.
Es wird eins markiert und bei Druck der Taste "Enter",wird der Name bzw. Daten des markierten Radiobutton in eine andere Form übernommen und diese Form mit den Radiobuttons closed sich dann.

Wenn Ich den Vorgang jetzt aber 3-4 mal durchführe erhalte Ich eine "Access Violation / Zugriffsverletzung".
Ich analysiere schon seit Tagen das Problem und ich weiss nicht mehr wo Ich noch suchen soll und deshalb wende Ich mich an euch.
Also, wie gesagt, ich vermute den Fehler an : procedure TfmDynArt.Dyn_Zusatz_KeyPress(Sender: TObject; var Key: Char);

...Was sagt Ihr dazu ?

Für Rückfragen stehe Ich euch natürlich zur Verfügung.

Jagg[meta]Test[/meta]

Moderiert von user profile iconGausi: URL- durch Delphi-Tags ersetzt


Xentar - Sa 06.12.08 05:33

[URL] <-- Wat junge?!
URL steht für einen Link zu einer Internetseite, DELPHI für einen Code in gleichnamiger Programmiersprache. Also: Versuch's nochmal ;)

Hast du den Code mal im Compiler, also von Delphi aus ausgeführt? Der zeigt doch normalerweise direkt die Zeile an, wo der Fehler auftritt.


jaenicke - Sa 06.12.08 13:17

Erstens:
Wenn du die RadioButtons so erstellst, dann solltest du sie nicht selbst freigeben, das gibt dann Probleme:

Delphi-Quelltext
1:
Dyn_Zusatz[iDyn_Zusatz] := TRadioButton.Create(fmDynArt);                    
So sagst du, dass du einen neuen RadioButton erstellen willst, um dessen Verwaltung (inkl. Freigabe) sind fmDynArt kümmern soll.

Wenn du die Verwaltung selber übernehmen willst, dann gib stattdessen nil an.

Delphi-Quelltext
1:
Dyn_Zusatz[iDyn_Zusatz] := TRadioButton.Create(nil);                    


Zweitens:
Die globalen Variablen machen den Code nicht gerade lesbarer...
Zum Beispiel wird NameCopy in Dyn_Zusatz_KeyPress nie zugewiesen, dann aber benutzt.

Drittens:
Es ist sehr schlechter Stil Daten als String in die RadioButtons zu legen und dann auszulesen. Speichere das doch lieber in einem zusätzlichen Array, zum Beispiel in Records. Dann wird dein Code sehr viel kürzer und übersichtlicher, weil du dir die ganzen Umwandel- und Stringoperationen sparst.
// EDIT:
Ach ja: Zur Identifikation des RadioButtons (um den Index im Array zu finden) kannst du die Eigenschaft Tag benutzen. In der kannst du bei jeder Komponente einen Integerwert speichern, den du frei verwenden kannst.

Viertens:
Dein (Sender as TRadiobutton).Caption:
as überprüft jedesmal ob die Umwandlung möglich ist. Besser ist es dies einmal mit is zu prüfen und danach immer direkt zu casten:

Delphi-Quelltext
1:
2:
3:
if Sender is TRadioButton then
begin
  TRadiobutton(Sender).Caption...

Noch besser hier: speichere die Caption einmal am Anfang von Dyn_Zusatz_KeyPress in einen String und arbeite mit dem, sonst wird jedesmal der Typecast (bei dir mit as) und der Zugriff auf die EIgenschaft Caption gemacht. Das bremst deinen Code nur unnötig und macht ihn unübersichtlicher, weil jedesmal das lange (Sender as TRadiobutton).Caption statt einer kürzeren Variable benutzt wird.


Jagg - Sa 06.12.08 20:08

@Xentar :
Wenn diese "Access Violation" Fehlermeldung auftritt, dann springt Delphi zu KEINER Zeile.
Es steht da nur diese Meldung !!!

@jaenicke :
Ich danke dir erstmal für die Verbessrungsvorschläge, ich werde diese dann auch beachten...aber anderseits, wie helfen Mir deine Verbesserungsvorschäge ?

Zu "Erstens" :
Zitat:
Erstens:
Wenn du die RadioButtons so erstellst, dann solltest du sie nicht selbst freigeben, das gibt dann Probleme.
1: Dyn_Zusatz[iDyn_Zusatz] := TRadioButton.Create(fmDynArt);

So sagst du, dass du einen neuen RadioButton erstellen willst, um dessen Verwaltung (inkl. Freigabe) sind fmDynArt kümmern soll.

Wenn du die Verwaltung selber übernehmen willst, dann gib stattdessen nil an.
1:Dyn_Zusatz[iDyn_Zusatz] := TRadioButton.Create(nil);


...ich habe das mit dem NIL ausprobiert, der Fehler tritt immer noch auf.

I NEED YOUR HELP !!!

Jagg


Xentar - Sa 06.12.08 20:23

Schrei doch nicht so, wir sind doch nicht blind...

Dann anders:
Wenn die Access Violation auftritt, steht da normalerweise "EAccessViolation an Adresse 0x********", die merkst du dir, und gehst dann in Delphi Suchen -> Laufzeitfehler suchen (je nach Delphi Version auch "Zu Zeile springen"), und tippst dort die Adresse ein, dann sollte Delphi dir auch die Zeile anzeigen.


Jagg - Sa 06.12.08 20:42

Also, die Fehlermeldung lautet :

Zitat:
Zugrifssverletzung bei Adresse 00146737, Lesen von Adresse 0043181E


...Wenn Ich diese Adresse suche, kommt bei mir ein kleines CPU Fenster, wo viele Zahlen drinstehen, ich kann damit aber nix anfangen.

Ausserdem die Adressnummern sind immer unterschiedlich.

@Xentar : Ich korrigiere, es muss heissen : "Schrei nicht so, wir sind nicht TAUB" :)

Sorry, aber ich bin etwas genervt wegen der Sache.

Jagg


jaenicke - Sa 06.12.08 21:23

Nun ja, wenn es schon beim 3.-4. Mal auftritt, dann wäre die einfachste Lösung wohl einfach ein Haltepunkt in dem KeyPress und dann Einzelschritt mit F7/F8 bis der Fehler auftritt.

Ansonsten bleibt nur überall wo es auftreten könnte Logeinträge beim Start und Ende der Prozeduren zu machen, so dass du dann feststellst wo der Fehler auftritt. Dabei kannst du die Datei ja jedesmal öffnen, die neue Zeile anhängen und die Datei wieder schließen, denn die Performance ist dabei ja egal.

Dann gibt es da noch Tools wie dieses, die dir die Zeilennummern von Fehlern ausgeben, sowas kann sich auch für das Ausliefern einer Software lohnen, auch wenn das dann bei kommerzieller Nutzung Geld kostet.
http://www.madshi.net/madExceptDescription.htm


Jagg - So 07.12.08 02:49

Ich habe mal ne generelle Frage :

Wenn Ich KEIN TRadiobutton erzeuge, z.b Dyn_Zusatz[1].Create...aber es dann später trotzdem per FREE freigebe, erhalte ich dann eine Access Violation bzw. Zugriffverletzung ?

Jagg


jaenicke - So 07.12.08 02:58

Das kann passieren, aber der Fall sollte bei einer sauberen Programmierung ja ohnehin nicht auftreten.

Generell überprüft Free vorher ob die Referenz nil ist, wenn aber ein anderer Wert als nil drin steht, das Objekt aber nicht gültig ist, zum Beispiel weil nicht FreeAndNil benutzt wurde um ein Objekt freizugeben, dann gibt es eine Zugriffsverletzung beim Aufruf von Free.


BenBE - Mo 08.12.08 15:48

user profile iconJagg hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe mal ne generelle Frage :

Wenn Ich KEIN TRadiobutton erzeuge, z.b Dyn_Zusatz[1].Create...aber es dann später trotzdem per FREE freigebe, erhalte ich dann eine Access Violation bzw. Zugriffverletzung ?

Jagg

Darf ich Schmerzensgeld verlangen? Das tut einem ja richtig weh! Instanzvariable := Klassenname.Create(Parameter); ist die korrekte Variante