Autor Beitrag
Bronstein
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 578
Erhaltene Danke: 1

WIN XP
Delphi 6 / Delphi 2006 / Delphi XE
BeitragVerfasst: Mi 21.11.07 17:50 
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?

ausblenden volle Höhe 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

_________________
Es gibt keine dummen Fragen nur dumme Antworten!!!
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mi 21.11.07 19: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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 578
Erhaltene Danke: 1

WIN XP
Delphi 6 / Delphi 2006 / Delphi XE
BeitragVerfasst: Mi 21.11.07 21:24 
Wenn ich aber die Zeilen 32 - 95 auskommentiere funktioniert es!

Wie mach ich das?

ausblenden 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?

_________________
Es gibt keine dummen Fragen nur dumme Antworten!!!
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mi 21.11.07 23: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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 578
Erhaltene Danke: 1

WIN XP
Delphi 6 / Delphi 2006 / Delphi XE
BeitragVerfasst: Do 22.11.07 07:45 
Hallo,
stimmt das wie ich es oben geschrieben habe?

Ich komm einfach nichtmehr weiter!

_________________
Es gibt keine dummen Fragen nur dumme Antworten!!!
gispos
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 94

WIN 7
XE10, D2007
BeitragVerfasst: Do 22.11.07 18: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:
ausblenden 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