Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Exception EOSError Systemfehler Code 1158


D. Annies - Do 20.01.11 20:10
Titel: Exception EOSError Systemfehler Code 1158
Hi, Delpher,

ich kopiere per Delphi-Programm viele Dateien z.B. von D nach H.
Uv's werden angelegt, alles läuft bestens.
Gegen Ende der umfangreichen Transaktion tritt der obige Fehler auf mit der Erläuterung:

Der aktuelle Prozess verwendet alle Handles der zulässigen Höchstanzahl für Windows Managerobjekte
Prozess wurde angehalten ...

Was ist denn da los?

Gruß, Detlef


jaenicke - Do 20.01.11 20:53

Das bedeutet wohl, dass du die Dateihandles beim Kopieren nicht wieder freigibst. Und wenn alle verfügbaren Handles von dir in Beschlag genommen sind, ist Schluss.


D. Annies - Do 20.01.11 21:41

Klingt sehr plausibel!
Woran erkenne ich ein Dateihandle? Wie vergebe / löse ich ein Dateihandle?
Gruß, Detlef


jaenicke - Do 20.01.11 21:44

Das kommt ja drauf an wie du kopierst...

Nebenbei zeigen der Taskmanager bzw. der Process Explorer auch an wie viele Handles ein Programm gerade verwendet.


D. Annies - Do 20.01.11 21:56

Ich kopiere folgendermaßen:


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:
if (fileexists(home_filename)) and (fileexists(work_filename)) then
          begin

            ///////////////////////////  ! Stunde So/Wi wegcodieren

            if (FileDateToDateTime(FileAge(work_filename)) <> FileDateToDateTime(FileAge(home_filename))) and
                            (fileage(work_filename) + 3600 <> FileAge(home_filename)) and
                            (fileage(work_filename)        <> FileAge(home_filename) + 3600then
            begin
              erg := xmessagedlg('ArchivBit wurde gesetzt' +#13 +
                       'Soll    ' + work_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(work_filename))) +#13+
                       'durch '   + home_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(home_filename))) +#13+
                       ' ersetzt werden?', mtConfirmation, [mbYes, mbno, mbcancel], ['ja''nein''Abbrechen'], self.font);
                                                             //  ???
              case erg of mryes    : CopyFile(PChar(home_filename), PChar(work_filename), FALSE);
                          mrcancel : raus := true;
              end;
            end
            // else showmessage(work_filename + ' ist von der Zeitumstellung betroffen');
          end             {CreateDir oder forcedirectories(label30.caption+uvname);}
          else
          if (fileexists(home_filename)) and (not fileexists(work_filename)) then
          begin               // Verzeichnisname + FindRec.Name
            erg := xmessagedlg('Soll '+ work_filename + #13' angelegt werden?', mtConfirmation,
                     [mbYes, mbNo, mbcancel], ['ja''nein''Abbrechen'], self.font);
            case erg of mryes    : begin                  //label30.caption+uvname
                                     if not directoryexists(ziel+uvname) then
                                       CreateDir(ziel+uvname);
                                     CopyFile(PChar(home_filename), PChar(work_filename), TRUE);
                                   end;
                        mrcancel : raus := true;
            end;
          end;

Das Handle ist wohl der PChar (??)
(Wie) Geht es anders / besser?
Gruß, Detlef

Moderiert von user profile iconNarses: Delphi-Tag repariert.


jaenicke - Do 20.01.11 22:02

Da ist kein Handle, das du explizit benutzt. In den verwendeten Delphifunktionen wird das ziemlich sicher alles richtig freigegeben.

Ich habe aber einen Verdacht:
Ich rate einmal, dass du FindFirst usw. benutzt um nach den Dateien zu suchen. Kann es sein, dass du vergessen hast, diese Suchhandles mit FindClose auch wieder zu schließen? ;-)


D. Annies - Do 20.01.11 23:05

Ich dachte erst: Volltreffer, aber ich glaube, nicht:
Die komplette Proc:


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:
105:
106:
107:
108:
109:
Procedure TBuchMain.FindFiles (aPath, aFindMask: String; aWithSub: Boolean);
Var  FindRec                       : tSearchRec;
     home_filename, work_filename,
     Pfad, uvname, start, ziel     : string;
     n, erg                        : integer;
     raus                          : boolean;
Begin
  raus := false;
  If (aPath = ''or (aFindMask = ''then Exit;
  If aPath[Length(aPath)] <> '\' Then  aPath := aPath + '\';
  Pfad := aPath;
  If FindFirst (aPath + aFindMask, faAnyFile - faDirectory, FindRec) = 0 Then
  Repeat
    If (FindRec.Name <> '.'and (FindRec.Name <> '..'Then
    begin
      if aktuser <> 'an s' then                           // normaler User
      begin           // die benötigten Projektdateien
        if (pos('home.', FindRec.Name) > 0or
           (pos('.pas',  FindRec.Name) > 0or (pos('.dfm',  FindRec.Name) > 0or
           (pos('.res',  FindRec.Name) > 0or (pos('.exe',  FindRec.Name) > 0or
           (pos('.dpr',  FindRec.Name) > 0)
          OR          // die nicht benötigten Projektdateien
           (pos('.ddp',  FindRec.Name) > 0or (pos('.dof',  FindRec.Name) > 0or
           (pos('.dsk',  FindRec.Name) > 0or (pos('.dcu',  FindRec.Name) > 0or
           (pos('.cfg',  FindRec.Name) > 0then
        begin  end    // keine PrgDatei weitergeben
      end
      else                                                // SuperUser
      begin
        if (pos('.ddp',  FindRec.Name) > 0or  (pos('.dof',  FindRec.Name) > 0or
           (pos('.dsk',  FindRec.Name) > 0or  (pos('.dcu',  FindRec.Name) > 0or
           (pos('.cfg',  FindRec.Name) > 0then
        begin  end
        else
        if (FindRec.Name[1] = '_')             or (pos('.~', FindRec.Name) > 0)    or
           (pos('bestand_', FindRec.Name) > 0or (pos('temp_', FindRec.Name) > 0or
           (pos('Geburtstag_', FindRec.Name) > 0then
        begin
          showmessage(apath + findrec.name + ' wird gelöscht');
          DeleteFile(apath + FindRec.name)
        end
        else
        begin
          uvname := '';                            //aPath
          for n := pos('Buecherei\', Pfad)+10 to length(Pfad) do
            uvname := uvname + apath[n];
                                                                  // Stick nach Platte(oder 2.Stick)
          if aPath[1] = stdwert[1then  // START, von zB. C: nach zB. F: = ArbeitsVerz.
          begin
            start := stdwert; ziel := label30.Caption;
            home_filename := stdwert + uvname + FindRec.Name;
            work_filename := label30.caption + uvname + FindRec.name;
          end
          else //if aPath[1] = label30.Caption[1] then  // ENDE, von zB. F: nach zB. C: = Rücksicherung
          begin
            start := label30.Caption; ziel := stdwert;
            home_filename := label30.caption + uvname + FindRec.name;
            work_filename := stdwert + uvname + FindRec.name;
          end;
                                                  // getTimeZoneInformation, DateiAttribute
          if (fileexists(home_filename)) and (fileexists(work_filename)) then
          begin

            ///////////////////////////  ! Stunde So/Wi wegcodieren

            if (FileDateToDateTime(FileAge(work_filename)) <> FileDateToDateTime(FileAge(home_filename))) and
                            (fileage(work_filename) + 3600 <> FileAge(home_filename)) and
                            (fileage(work_filename)        <> FileAge(home_filename) + 3600then
            begin
              erg := xmessagedlg('ArchivBit wurde gesetzt' +#13 +
                       'Soll    ' + work_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(work_filename))) +#13+
                       'durch '   + home_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(home_filename))) +#13+
                       ' ersetzt werden?', mtConfirmation, [mbYes, mbno, mbcancel], ['ja''nein''Abbrechen'], self.font);
                                                             //  ???
              case erg of mryes    : CopyFile(PChar(home_filename), PChar(work_filename), FALSE);
                          mrcancel : raus := true;
              end;
            end
            // else showmessage(work_filename + ' ist von der Zeitumstellung betroffen');
          end             {CreateDir oder forcedirectories(label30.caption+uvname);}
          else
          if (fileexists(home_filename)) and (not fileexists(work_filename)) then
          begin               // Verzeichnisname + FindRec.Name
            erg := xmessagedlg('Soll '+ work_filename + #13' angelegt werden?', mtConfirmation,
                     [mbYes, mbNo, mbcancel], ['ja''nein''Abbrechen'], self.font);
            case erg of mryes    : begin                  //label30.caption+uvname
                                     if not directoryexists(ziel+uvname) then
                                       CreateDir(ziel+uvname);
                                     CopyFile(PChar(home_filename), PChar(work_filename), TRUE);
                                   end;
                        mrcancel : raus := true;
            end;
          end;
        end;
      end
    end;
  Until FindNext(FindRec) <> 0;
  FindClose(FindRec);

  If (Not aWithSub) or (raus) Then  Exit;    // keine UV's oder Abbruch dann raus
  If FindFirst (aPath + '*.*', faAnyFile, FindRec) = 0 Then
  Repeat     // In Unterverzeichnissen weiter suchen
    If (FindRec.Name <> '.'and (FindRec.Name <> '..'Then
      If Boolean (FindRec.Attr and faDirectory) Then  // wenn es sich um ein Verzeichnis handelt
        If ((FindRec.Attr and faDirectory) <> 0then
          FindFiles(aPath + FindRec.Name, aFindMask, aWithSub);  // Rekursion
  Until FindNext(FindRec) <> 0;
  FindClose(FindRec);
End;


Uff, Detlef


jaenicke - Do 20.01.11 23:38

Dann bleibt nur im Taskmanager bzw. besser dem Process Explorer auf die Handles zu achten und zu schauen ob und wo deren Anzahl zunimmt.


D. Annies - Fr 21.01.11 07:42

Ich habe keine Ahnung, wie ich dabei vorgehen könnte. :(((

(Nun, den Process Manager habe ich, aber damit umgehen kann ich nicht)


jaenicke - Fr 21.01.11 08:08

Im Anhang einmal ein Screenshot. Das sind die Eigenschaften eines Prozesses. Und dort steht die Handleanzahl. Wenn du jetzt im Debugger schrittweise durchgehst, siehst du wo diese steigt oder sinkt.
Und wenn sie bei der selben Operation immer weiter steigt und nie wieder auf den alten Wert zurückgeht, stimmt etwas nicht.


D. Annies - Fr 21.01.11 10:54

Es läuft doch darauf hinaus, dass ich da im Code einen Logikfehler eingebaut habe,
kann den jemand auch so erkennen? - Ich vermute ihn bei der Rekursion am Proc-Ende.
Ich krieg den Fehler nicht raus :( (trotz guter Hilfe bisher)
Detlef


zuma - Fr 21.01.11 11:49

Ich find merkwürdig, das du eine Methode 'FindFiles' hat, die sucht UND kopiert, beides in einem. Mein Lösungsansatz wäre gewesen, 2 Methoden zu haben:

Eine Methode, die die zu kopierenden Dateien ermittelt (und wirklich auch nur das macht) und z.b als Liste wieder gibt
und
eine Methode, die anhand einer übergebenen Liste Dateien kopiert (ala kopiere(xDateiliste, xZiel).

dein Findclose wird erst nach abarbeiten der repeatschleife aufgerufen, du kopierst aber innerhalb der schleife.


D. Annies - Fr 21.01.11 12:36

Hi, zuma,
das ist richtig, aber wenn ich das Findclose in die Schleife setze, wird immer nur eine Datei
pro Verzeichnis kopiert.
Es sind zwar 1348 Dateien insgesamt, aber nicht > 1000 in einem UV!
Detlef


zuma - Fr 21.01.11 14:38

Hallo Detlef,
das ist richtig, aber mein Lösungsvorschlag war ja auch nicht, das FindClose in die Schleife zu verlagern, sondern deine Schleife in 2 aufzuteilen. In der jetzigen Variante wirste das Kopieren raus und merkst dir dort stattdessen die Dateie, die du kopieren möchtest (z.B. in einer Stringlist). Das Ergebnis (also die gefüllte Stringlist) übergibst du dann an die 2te, neu zu erstellende Methode, die dann das eigentliche Copy macht.


D. Annies - Fr 21.01.11 19:45

hab jetzt eine Menge versucht, noch ohne Erfolg: Ich scheitere an der Erstellung der Proc,
die dann für das Kopieren en bloc zuständig sein soll. Wobei ja kopiert oder ersetzt werden soll!
Wie könnte die aussehen, sprich welcher Befehl gilt da? CopyFile(), CopyFileEx() oder was?


jaenicke - Fr 21.01.11 20:04

An SHFileOperation könntest du auch gleich mehrere Dateien auf einmal übergeben.

Welchen Befehl du da nutzt, ist im Grunde nicht so wichtig. Wobei ich vermute, dass SHFileOperation mit jeweils mehreren Dateien einen Geschwindigkeitsvorteil bringen könnte.


BenBE - Fr 21.01.11 20:32

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
An SHFileOperation könntest du auch gleich mehrere Dateien auf einmal übergeben.

Welchen Befehl du da nutzt, ist im Grunde nicht so wichtig. Wobei ich vermute, dass SHFileOperation mit jeweils mehreren Dateien einen Geschwindigkeitsvorteil bringen könnte.

Ist in der Regel 5%, teilweise bis zu 15% langsamer als die Dateien selbst mit geeigneter Puffergröße zu kopieren. Hatte da mal selber mir ein Tool geschrieben, was immer Meilenweit vor dem Explorer fertig war.


D. Annies - Fr 21.01.11 20:40

Aber leider nicht mit D6Enter. :(


D. Annies - Sa 22.01.11 08:17

So, ich hab's gelöst:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
if (fileexists(home_filename)) and (not fileexists(work_filename)) then
          begin               // Verzeichnisname + FindRec.Name
            erg := xmessagedlg('Soll '+ work_filename + #13' angelegt werden?', mtConfirmation,
                     [mbYes, mbNo, mbcancel], ['ja''nein''Abbrechen'], self.font);
            case erg of mryes    : begin                  //label30.caption+uvname
         
                                     if not directoryexists(ziel+uvname) then
                                       CreateDir(ziel+uvname);
                                     CopyFile(PChar(home_filename), PChar(work_filename), TRUE);

                                   end;
                        mrcancel : raus := true;
            end;
          end;


Ich lasse die xmessagedld-Abfrage weg, nur die drei Zeilen "in der Mitte" lasse ich stehen, damit auf jeden Fall
(ohne Nachfrage) kopiert wird!
Yes! Uff!
Gruß, Detlef
P.S. Weiß jemand, warum das die Handles "spart"?


jaenicke - Sa 22.01.11 09:10

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
P.S. Weiß jemand, warum das die Handles "spart"?
Naja, ich weiß ja nicht was xmessagedlg für eine Funktion ist, aber ich könnte wetten, dass da etwas nicht freigegeben wird...
Vielleicht der angezeigte Dialog selbst? Der beinhaltet natürlich gleich mehrere Handles und Objekte.

Mit FastMM kannst du solche nicht freigegebenen Sachen aber auch finden.


D. Annies - Sa 22.01.11 10:21

Hi, Sebastian,
danke für deine Nachsorge, hier der xMessageDlg-Code.
Bin mal neugierig, was du findest:


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:
function TBuchMain.xMessageDlg(const Msg: string; DlgType : TMsgDlgType;
            Buttons : TMsgDlgButtons; Captions: array of string; myFont:TFont) : Integer;
var aMsgDlg         : TForm;
    CaptionIndex, i : integer;
    dlgButton       : TButton;  // uses stdctrls
begin
  aMsgDlg := CreateMessageDialog(Msg, DlgType, buttons);
  aMsgDlg.Font.style := [];   //myFont, zB. [fsitalic] ;
  aMsgDlg.setbounds(300,520,650,200);
  CaptionIndex := 0;
  for i := 0 to aMsgDlg.ComponentCount - 1 do  // alle Objekte des Dialoges testen
  begin
    if (aMsgDlg.Components[i] is TButton) then  // wenn es ein Button ist, dann...
    begin
      dlgButton := TButton(aMsgDlg.Components[i]);
      dlgButton.Left := 100*(i-2) + 10*(i+1);
      dlgbutton.width := 100;   // default=68;
      if CaptionIndex > High(Captions) then Break;
      dlgButton.Caption := Captions[CaptionIndex]; // Beschriftung = Captions-array
      Inc(CaptionIndex);
    end;
  end;
  Result := aMsgDlg.ShowModal;
end;


Bis denne, Detlef


jaenicke - Sa 22.01.11 11:25

aMsgDlg wird nicht freigegeben.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
aMsgDlg := ...
try
  ...
  Result := aMsgDlg...
finally
  aMsgDlg.Free;
end;


D. Annies - Sa 22.01.11 14:30

Danke, Sebastian! - das heißt also: jedesmal, wenn ich etwas der Form .create habe,
sollte ich mit try .. except arbeiten und mit free, richtig?
Detlef


jaenicke - Sa 22.01.11 16:17

Mit try..finally. ;-) Es sei denn du hast einen Owner angegeben. In diesem Fall gibt dieser Owner bei dessen Zerstörung das Objekt frei. Wenn du das selbst machen möchtest, solltest du nil angeben.

In diesem Fall hier wird innerhalb CreateMessageDialog Application als Owner verwendet. Das heißt beim Beenden der Anwendung würde der Dialog auch freigegeben. Aber eben erst dann, und wenn du zu viele solcher Dialoge erzeugst, bleiben die alle bis dahin im Speicher. ;-)


D. Annies - So 23.01.11 21:57

Danke, Sebastian