Entwickler-Ecke

Sonstiges (Delphi) - EAccessViolation keine Ahnung warum


Bronstein - Mi 21.11.07 18:50
Titel: EAccessViolation keine Ahnung warum
Hallo,
ich habe ein Problem.

Ich rufe die Funktion siehe unten in einem Thread mehrmals auf und bekomme einen Fehler EAccessViolation.

Wenn ich das die funktion durchgehe (debug), bekomme ich den Fehler erst wenn ich die Funktion verlasse, woran kann das liegen bzw. wo liegt der Fehler?


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:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
function test(SetupName, LineName: Pchar): Pchar; stdcall;
var
  tmpStr, Spur, Bauteil: ShortString;
  Dateiname: STring;
  ini: TiniFile;
  SL, Datei: TStringList;
  i, k, z, y, f: Integer;
  XML, XMLCencorp, tmpXML: String;
  checkStart: Boolean;
  xmlDoc: IXmlDocument;
  SR: TSearchRec;
  tmpPchar: Pchar;
begin
  result := 'Error';
  try
    FehlerProtokoll(LineName);
    //Hier den Wert der aktuellen Linie holen
    tmpStr := ExtractFilePath(ParamStr(0)) + 'Cencorp_MobiCheck.ini';
    if FileExists(tmpStr) then
    begin
      ini:=TIniFile.Create(tmpStr);
      tmpStr := '';
      //Maschinen der Linie ermitteln
      SL := TStringList.Create;
      SL.Delimiter := ';';
      tmpStr := '';
      tmpStr := ini.ReadString('Einstellungen', LineName, tmpStr);
      SL.DelimitedText := tmpStr;

      tmpXML := '';
      XML := '<Setup Name="' + SetupName + '" LineName="' + LineName + '">';
      for i := 0 to SL.Count-2 do
      begin
        tmpXML := '';
        XML := XML + '<Module Name="' + SL[i] + '">';
        XML := XML + '<FeederTable Name = "1">';
        tmpStr := '';
        tmpStr := ini.ReadString('Einstellungen', SL[i] + '_Feeder''');
        //prüfen um was für einen Typ es sich handelt Cencorp oder PMJ
        if tmpStr = '' then
        begin
           Application.ProcessMessages;
          Dateiname := '';
          tmpStr := ini.ReadString('Einstellungen', SL[i] + '_works''');

          if FindFirst(tmpStr + '\*' + SetupName + '*.wls', faAnyFile, SR) = 0 then
            Dateiname := Sr.Name;
          if FindFirst(tmpStr + '\*' + copy(SetupName, 12) + '_' + copy(SetupName, 33) + '_' + Copy(SetupName, 63) + '*.wls', faAnyFile, SR) = 0 then
            Dateiname := Sr.Name;
          Application.ProcessMessages;
          if Dateiname <> '' then
          begin
            //FehlerProtokoll(tmpStr + '\' + Dateiname);
            Datei := TStringList.Create;
            Datei.LoadFromFile(tmpStr + '\' + Dateiname);
            for k:=0 to Datei.Count-1 do
            begin
              //if IstInString(Datei[k], '#') then
              if pos('#', Datei[k]) > 0 then
              begin
                Spur := copy(Datei[k], length(Datei[k])-12);
                Bauteil := '';
                for z:=0 to Length(Datei[k])-1 do
                begin
                  if Datei[k][z] = '#' then
                    break;
                  if IsValidInteger(Datei[k][z]) then
                    Bauteil := Bauteil + Datei[k][z];
                end;
                if not IstInString(tmpXML, Bauteil) then
                begin
                  tmpXML := tmpXML + '<ScanTrack Nr="' + Spur + '">';  //Spur
                  tmpXML := tmpXML + '<Component>' + Bauteil + '</Component>';
                  tmpXML := tmpXML + '<Feeder>';
                  tmpXML := tmpXML + '<FeederTrack>'+Spur+'</FeederTrack>';
                  tmpXML := tmpXML + '<Pocket>1</Pocket>';   //Fach immer 1
                  tmpXML := tmpXML + '</Feeder>';
                  tmpXML := tmpXML + '</ScanTrack>';
                end;
              end;
            end;
            Application.ProcessMessages;
            //Datei.Free;
          end
          else
            FehlerProtokoll('Keine Programm bei der Maschine:' + SL[i] + ' beim Programm ' + SetupName + ' vorhanden. Pfad: ' + tmpStr);
        end
        tmpXML := tmpXML + '<ScanTrack Nr="04"><Component>905552</Component>';
        tmpXML := tmpXML + '<Feeder><FeederTrack>04</FeederTrack><Pocket>1</Pocket></Feeder></ScanTrack>';

        end;
        XML := XML + tmpXML;
        XML := XML + '</FeederTable>';
        XML := XML + '</Module>';
      end;
      XML := XML + '</Setup>';
      //SL.Free;
      Result := PChar(string(XML));

    end;
  except
    FehlerProtokoll('GetSetup: unbekannter Fehler');
  end;
end;



Moderiert von user profile iconjasocul: Topic aus VCL (Visual Component Library) verschoben am Do 22.11.2007 um 07:46


delfiphan - Mi 21.11.07 20:10

Du kannst keinen PChar zurückliefern, der aus einem String gecastet wurde. Der String wird beim Verlassen freigegeben, da out of scope, und somit wird der Pointer ungültig.

Mach's so wie die WinAPI: Der User soll den Speicher allozieren, nicht die Funktion. Achtung Bufferoverflow-Gefahr. Anders geht's fast nicht, da der Delphi-Compiler nicht wissen kann, wann der PChar freigegeben werden kann.


Bronstein - Mi 21.11.07 22:24

Wenn ich aber die Zeilen 32 - 95 auskommentiere funktioniert es!

Wie mach ich das?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function test(SetupName, LineName: Pchar): Pchar; stdcall;
var
  pText: Pchar;
[...]
pText := StrAlloc(Length(XML) + 1);
StrPLCopy(pText, XML, Length(XML));
result := pText;


Wenn ich was mit StrAlloc mache, muss ich das doch auch wieder freigeben (StrDispose) wo mach ich das dann?


delfiphan - Do 22.11.07 00:11

Es funktioniert, weil der Memorymanager den Bereich noch nicht freigegeben hat sondern noch für evtl. weitere Daten warmhält! Grosse Blöcke werden entsprechend sofort freigegeben.

Der Compiler/Memorymanager hat keine Chance rauszufinden, wie lange der PChar gebraucht wird, da er weder referenzgezählt noch sonst wie gegen Memory-Leaks geschützt ist. Den musst du selbst freigeben, sobald du ihn nicht mehr brauchst.

Die Lösung zum Problem habe ich oben beschrieben: User alloziert Memory selbst. Alternativ kannst du die Daten auch über einen Callback zurückstreamen.


Bronstein - Do 22.11.07 08:45

Hallo,
stimmt das wie ich es oben geschrieben habe?

Ich komm einfach nichtmehr weiter!


gispos - Do 22.11.07 19:09

Hallo bronstein,
also eine konkrete Hilfe hab ich nicht, deine Function ist recht abenteuerlich und
Fehler können sich hier an vielen Stellen einschleichen.
z.B. Datei öffnen. Sind denn auch alle Dateien vorhanden?

Versuche den Startpunkt des Debuger in der Schleife ab Zeile 32 zu setzen, dann sollte
es sich doch debugen lassen. Musst natürlich die DLL mit Hostanwendung debugen!

Zweiter Vorschlag, baue ein Try except in die Schleife (33 bis 94) ein, und verschiebe das Except dann immer weiter nach oben bis das Error ausgelößt wird.
Oder mache einfach ein "exit" ab Zeile 33 und verschiebe das "exit" immer weiter nach unten.

Die Stringlisten nicht freigeben, ist das Absicht?
Die ini wird auch nicht frei gegeben!
Wird die Function auch durch "stdcall" in der Anwendung aufgerufen?
In Zeile 87 oder 91 ist ein "end" zuviel!
FindFirst muss im Erfolgsfall durch SysUtils.FindClose frei gegeben werden!

Nachtrag:
Habe z.B. diesen Fehler gefunden:

Delphi-Quelltext
1:
2:
3:
4:
5:
 
  for z:= 0 to Length(Datei[k])-1 do ??

Richtig dann so:
  for z:= 1 to Length(Datei[k]) do

Frage? Was macht dein IsValidInteger(Datei[k][z]) wenn Datei[k][z] = '' ??

Gruß gispos