Autor Beitrag
J.Borchert
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 150

WIN10
XE2 Professional
BeitragVerfasst: Do 17.05.12 08:39 
Leider klappt mein Versuch der Rekursion innerhalb eines Threads nicht.

Ist das jetzt nur wegen des "Execute"-Aufruf, sollte ich so ziemlich alles was in Execute stattfindet in eine separate Funktion auslagern, die sich dann selbst aufrufen kann?

Und btw: Proceduren die in Synchronise() aufgerufen werden, können die auch Parameter haben?

Einen dennoch schönen Herrentag wünscht Jürgen

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:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
unit FFThread;

interface

uses
  Classes, SysUtils, ComCtrls;

type
  pNodeInfo = ^tNodeInfo;
  tNodeInfo = RECORD
    Path: boolean;
    Name: string;
  END;

type
  FindFileThread = class(TThread)
  private
    { Private-Deklarationen }
    PROCEDURE SetzeAnode;
    Procedure SetzeAnodeTMP;
  protected
    procedure Execute; override;
  end;

VAR SearchRec: tSearchRec;  NodeTmp: tTreeNode;  Info: pNodeInfo;
    i: integer;  MaskeOK : Boolean;  s : string;

implementation
uses Unit1, jobs1;

{ FindFileThread }

procedure setAnode(const i:integer;const aN:tTreeNode);
  begin aN.ImageIndex := i;
        aN.SelectedIndex := i;
        aN.StateIndex := i;
  end;

procedure FindFileThread.SetzeAnode;
begin aNode := aTree.Items.AddChildObject(aNode, SearchRec.Name, Info);
      setAnode(5, aNode); // Grund-Icon (Ordner)
end;
procedure FindFileThread.SetzeAnodeTMP;
begin NodeTMp := aTree.Items.AddChildObject(aNode, SearchRec.Name, Info);
      setAnode(9, nodeTMP);
          if POS('DOC', s) > 0 then setAnode(6, nodeTMP);
          if POS('FIX', s) > 0 then setAnode(7, nodeTMP);
          if POS('XLS', s) > 0 then setAnode(16, nodeTMP);
          if POS('CSV', s) > 0 then setAnode(3, nodeTMP);
          if POS('VER', s) > 0 then setAnode(13, nodeTMP);
          if POS('GEO', s) > 0 then setAnode(1, nodeTMP);
          if POS('HT', s) > 0 then setAnode(10, nodeTMP);
          if POS('TXT', s) > 0 then setAnode(8, nodeTMP);
          if POS('PDF', s) > 0 then setAnode(11, nodeTMP);
          if POS('JPG', s) > 0 then setAnode(12, nodeTMP);
          if POS('BMP', s) > 0 then setAnode(2, nodeTMP);
          if POS('GIF', s) > 0 then setAnode(12, nodeTMP);
          if POS('MPG', s) > 0 then setAnode(15, nodeTMP);
          if POS('AVI', s) > 0 then setAnode(15, nodeTMP);
          if POS('TIF', s) > 0 then setAnode(12, nodeTMP);
          if POS('ZIP', s) > 0 then setAnode(0, nodeTMP);
          if POS('RTF', s) > 0 then setAnode(15, nodeTMP);
end;

procedure FindFileThread.Execute;
BEGIN aTree.Items.BeginUpdate;
   // Wenn am Ende der Pfadangabe noch kein \ steht, dieses hinzufügen
  IF aPath[Length(aPath)] <> '\' THEN aPath := aPath + '\';
  IF FindFirst(aPath + '*.*', faDirectory, SearchRec) = 0 THEN
   BEGIN
    REPEAT
        // Wenn es sich um ein Verzeichnis handelt
      IF (SearchRec.Attr AND faDirectory = faDirectory) AND
        (SearchRec.Name[1] <> '.'AND
        (SearchRec.Name <> '_Privat'AND
        (SearchRec.Name <> '_Temp'AND
        (SearchRec.Name <> '_Help'AND
        (SearchRec.Name <> '_Config'THEN
       BEGIN
        IF (SearchRec.Attr AND faDirectory > 0THEN
         BEGIN
          New(Info);
          Info^.Path := true;
          Info^.Name := aPath + SearchRec.Name;
                // zum aktuellen Eintrag hinzufügen
          Synchronize(SetzeAnode);
         END;
            // Eintrag merken
        NodeTmp := aNode.Parent;
            // auf Untereinträge prüfen
        aPath := aPath + SearchRec.Name;
        Execute;
            // Eintrag wiederholen
        aNode := NodeTmp;
       END
      ELSE // Eintrag ist eine Datei
       Begin for i:=1 to AnzStr(' ',Maske) do
              if POS( strx(' ',Maske,i), uppercase(ExtractFileExt(SearchRec.Name))) > 0
               then begin MaskeOK:=True; break; end else MaskeOK:=False;
        if FileIsInDelList(SearchRec.Name)then MaskeOK:=False;// Liste der zu löschenden
        IF aWithFiles AND (SearchRec.Name <> '.'AND
          (SearchRec.Name <> '..'AND
          ( (MaskeOK=True) or
            (Maske='')
          )
        THEN
         BEGIN
          New(Info);
          Info^.Path := false;
          Info^.Name := aPath + SearchRec.Name;
          s := uppercase(ExtractFileExt(SearchRec.Name));
          Synchronize(SetzeAnodeTMP);
         END;
       end;
      // solange weiter bis keine wieteren Dateien/Verzeichniss gefunden werden
    UNTIL FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
   END;
  aTree.Items.EndUpdate;
END;

end.

_________________
Wer immer macht was er schon kann, bleibt immer das, was er schon ist. "H.Ford"
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 17.05.12 09:55 
user profile iconJ.Borchert hat folgendes geschrieben Zum zitierten Posting springen:
Ist das jetzt nur wegen des "Execute"-Aufruf, sollte ich so ziemlich alles was in Execute stattfindet in eine separate Funktion auslagern, die sich dann selbst aufrufen kann?
Das ist so oder so sinnvoll um die Funktionalität besser zu kapseln. Im Moment ist das ganze durch die globalen Variablen usw. extrem unübersichtlich und fehleranfällig.

BeginUpdate und EndUpdate hast du vergessen synchronisiert aufzurufen.

user profile iconJ.Borchert hat folgendes geschrieben Zum zitierten Posting springen:
Und btw: Proceduren die in Synchronise() aufgerufen werden, können die auch Parameter haben?
Nein, das wird normalerweise über ein privates Feld geregelt. Ein Beispiel, auch hinsichtlich der Anmerkungen unten:
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:
type
  TOnDoSomething = procedure(const AParam: stringof object;

  TMyThread = class(TThread)
  private
    FMyParam: string;
    FOnDoSomething: TOnDoSomething;
    FTree: TTreeView;
    procedure MyMethod;
  public
    constructor Create(ATree: TTreeView; AOnDoSomething: TOnDoSomething);
    procedure Execute; override;
  end;

// ...

constructor Create(ATree: TTreeView; AOnDoSomething: TOnDoSomething);
begin
  inherited Create(False);
  FTree := ATree;
  FOnDoSomething := AOnDoSomething;
end;

procedure TMyThread.MyMethod;
begin
  if Assigned(FOnDoSomething) then
    FOnDoSomething(FMyParam);
end;

procedure TMyThread.Execute;
var
  i: Integer; // lokal deklariert reicht
begin
  FMyParam := 'Example';
  Synchronize(MyMethod); // MyMethod ruft die eigentliche Methode mit dem gespeicherten Parameter auf

  FTree. ... // FTree wurde im Konstruktor übergeben und steht daher bereit
end;
Ein paar Hinweise:
  • Typnamen sollten per Konvention grundsätzlich mit T anfangen --> TFindFileThread statt FindFileThread (Ausnahme sind Exceptions, die fangen mit E an)
  • Globale Variablen sollten der Übersichtlichkeit halber soweit als möglich vermieden werden. Insbesondere z.B. bei deiner Variablen i macht es auch gar keinen Sinn die Variable global zu deklarieren, da sie sowieso nur lokal in Execute verwendet wird. Dass aNode, aTree und aPath auch noch in einer anderen Unit deklariert sind, macht das ganze vollkommen unüberschaubar.
    Wer schreibt wann und wo die Variablen? Wer liest sie? ...
    Alles das kann man erst nach genauer Analyse des Quelltextes erkennen. Das dann auch noch in Verbindung mit Threads...
  • Eine einheitliche und übersichtlichere Codeformatierung erleichtert das Programmieren enorm, auch wenn es anfangs vielleicht nach mehr Arbeit aussieht. Man spart die aber durch besseren Überblick, wenn man später wieder auf den Code schaut, usw. später meist mehrfach wieder ein.
  • Besser als den Baum direkt synchronisiert anzusprechen wäre das eigentliche Hinzufügen z.B. in einem Event zu machen. Also wie im Beispiel FOnDoSomething. Das könnte eine in der Formularklasse deklarierte Methode sein.
    Aber das ist erst einmal nicht so wichtig.

Bei neueren Delphiversionen gibt es noch bessere Möglichkeiten, die die Synchronisation verbessern und vereinfachen, aber ich schätze mal die Delphiversion 7 im Profil stimmt noch, oder? Deshalb lasse ich das mal alles außen vor.
J.Borchert Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 150

WIN10
XE2 Professional
BeitragVerfasst: Do 17.05.12 18:38 
Danke für die ausführliche Hilfe.

Das mit dem .BeginUpdate usw. habe ich bereits bemerkt. Für mich war interessant, daß mein Thread trotzdem erstmal arbeitete, aber immer wenn er rekursiv (bei einem Verzeichnis) sich aufrufen wollte, brach er einfach komplett ab.
Das mit den Variablen ist wirklich blöd, daran werde ich erst mal arbeiten. Auch deine Hinweise (T.. E..) werde ich mir zu Herzen nehmen.

Danke nochmals für die Hilfe, vor allem für das Beispiel. Ich glaube so komme ich erst mal weiter.

Schönen TRestHerrenTag ohne EUeberGeben ;-) wünscht Jürgen

_________________
Wer immer macht was er schon kann, bleibt immer das, was er schon ist. "H.Ford"