Announcement

Collapse
No announcement yet.

Erkennen, wenn Dateien in ein Verzeichnis kopiert werden

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Erkennen, wenn Dateien in ein Verzeichnis kopiert werden

    Hallo, ich habe folgendes Problem. <br>
    Eine Anwendung legt in einem Verzeichnis csv Dateien an. Diese Verzeichnisse werden monatlich angeordnet. Pro Monat werden ca. 1500 csv Dateien angelegt.<br>
    Ich habe jetzt die Aufgabe diese Dateien einzulesen und bestimmte Daten daraus in eine Datenbank zuzufügen. <br>
    Ich habe mir gedacht, daß einfach mein Programm die ganze Zeit mitlauft, und den Ordner des jeweiligen Monats überwacht. Dazu gibt es ja unter Delphi 6 auch den ShellChangeNotifier, der mir ein Ereignis erzeugt, falls sich in einem Verzeichnis etwas tut. Hat einer eine Idee, wie ich von dem Ereignis am besten auf die geänderte (hinzugefügte) Datei komme ?

  • #2
    Hallo Frank, <BR>
    deine Anfrage hat mich an einen Artikel in der Zeitschrift Toolbox Ausgabe 2001/1 erinnert.<BR>
    Darin wird erklärt, wie man ein Verzeichnis überwacht.<BR>
    <BR>
    Als Stichwort kann ich dir an dieser Stelle die API-Funktion FindFirstChangeNotification nennen.

    HANDLE FindFirstChangeNotification(LPCTSTR lpPathName,<BR>
    BOOL bWatchSubtree,<BR>
    DWORD dwNotifyFilter);<BR>
    <BR>
    Unter Umständen kannst du den ganzen Artikel online auf der <BR>
    Homepage von Toolbox lesen.<BR>
    www.toolbox-mag.de<BR>
    <BR>
    Ich hoffe es hilft dir weiter<BR>
    Michael<BR&gt

    Comment


    • #3
      Moin Zusammen,<br>
      <br>
      ich kenne jetzt den ShellChangeNotifier nicht, allerdings erhält man mit FindFirstChangeNotification nur die Information, <b>dass</b> sich etwas getan hat.<br>
      Um zu erfahren um welche Datei es sich handelt, bleibt einem bei der Funktion nichts weiter übrig, als die enthaltenen Dateien, und ihre Attribute (dazu zähle ich hier auch Daten wie Änderungsdatum), selber zu verwalten.<br>
      Unter NT/W2K usw. könnte man ersatzweise ReadDirectroyChangesW einsetzen. Damit erhält man auch den Namen der Datei.<br>
      <br>
      Ciao<br>
      Chri

      Comment


      • #4
        Hallo Christian und Michael<br>
        vielen Dank für eure Hilfe. ShellChangeNotifier ist eine Beispielkomponente von Delphi 6, die eben auch über FindFirstChangeNotification mitteilt, dass sich etwas getan hat.<br>
        Das Programm soll aber sowieso unter W2K laufen. Christian, hast Du vielleicht auch noch ein Beispiel für ReadDirectroyChangesW ?<br>
        Gruss Fran

        Comment


        • #5
          Moin Frank,<br>
          <br>
          ja, hab ich:<br>
          <br>
          http://www.delphi-forum.de/forum/Forum20/HTML/000259.html<br>
          <br>
          Das Beispiel ist insofern nicht perfekt, als das der Aufruf von ReadDirectoryChangesW in einen Thread ausgelagert werden muss, da diese Funktion, bis zum Eintreffen eines Ereignisses genauso wartet, wie z.B. WaitForSingleObject / WaitForMultipleObjects bei FindFirstChange...<br>
          Dies sogar mit dem Nachteil, das kein Timeout angegeben werden kann.<br>
          Dieses Problem liesse sich wahrscheinlich durch asynchrone Nutzung umgehen (siehe Doku im PSDK), damit habe ich allerdings (noch) keine Erfahrung, und auch kein Beispiel.<br&gt

          Comment


          • #6
            Hallo Christian,<br>
            vielen Dank für deine Hilfe. Es ist genau das, was ich gesucht habe. Wenn ich das Beispiel in einen eigenen Thread ausgelagert habe, werde ich es hier posten. <br>Danke Fran

            Comment


            • #7
              Habe ich mal irgendwo gefunden:<br>
              ist nicht von mir !!<br>
              Ich benutze es um ein bestimmtes Verzeichnis zu überwachen<br>
              <b>Funktioniert 100%tig :O) </b>
              <br>
              <br>
              in unit 1 kommt:
              <br>
              ....
              <br>
              private
              <br>
              Kontrolle: TWatch;
              <br>
              .....

              <br>
              procedure TForm1.FormShow(Sender: TObject);
              <br>
              begin
              <br>
              kontrolle:= Twatch.Create('C:\Verzeichnis');
              <br>
              end;
              <br>
              <br>
              <br>
              <br>

              unit Unit2;
              <br>
              interface
              <br>
              uses
              <br>
              Classes,windows,dialogs,SysUtils;
              <br>
              type
              <br>
              TWatch = class(TThread)
              <br>
              private
              <br>
              changehandle:THandle;
              <br>
              watchpath : String;
              <br>
              { Private-Deklarationen }
              <br>
              protected
              <br>
              procedure Execute; override;
              <br>
              procedure RefreshListbox;
              <br>
              destructor Destroy;
              <br>
              public
              <br>
              constructor Create(path : String);
              <br>
              end;
              <br>
              implementation
              <br>
              uses unit1;
              <br>
              constructor TWatch.Create(path : String);
              <br>
              begin
              <br>
              inherited Create(False);
              <br>
              watchpath:= path;
              <br>
              FreeOnTerminate := True;
              <br>
              end;
              <br>
              procedure TWatch.Execute;
              <br>
              begin
              <br>
              changehandle:=FindFirstChangeNotification(PChar
              <br>
              (watchpath),FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
              <br>
              if changehandle <> INVALID_HANDLE_VALUE then begin
              <br>
              while True do begin // normalerweise eine Endlosschleife
              <br>
              if WaitForSingleObject(changehandle,500)= WAIT_OBJECT_0 then
              <br>
              begin
              <br>
              synchronize(RefreshListbox);
              <br>
              end;
              <br>
              FindNextChangeNotification(changehandle);
              <br>
              if Terminated then break;
              <br>
              end;
              <br>
              end;
              <br>
              end;
              <br>
              procedure TWatch.RefreshListbox;
              <br>
              // hier bitte anpassen !!!!
              // Irgendeine Aktion Auslösen etc.

              <br>
              begin
              <br>
              Form1.filebox.update;
              v
              Form1.Label7.Caption:= inttostr(Form1.filebox.Items.count);
              <br>
              Form1.statusbar.Panels[1].text:= 'Aktueller Eingang: ' + inttostr
              (Form1.filebox.Items.count)
              <br>
              end;
              <br>
              destructor TWatch.Destroy;
              <br>
              begin
              <br>
              if changehandle<> NULL then FindCloseChangeNotification
              <br>(changehandle);
              inherited Destroy;<br>
              end;
              <br>
              end

              Comment


              • #8
                Wie versprochen jetzt der Code. Es ist eine Zusammenfassung des Codes von Bernd und Christian.<br>
                Noch mal vilen Dank.<br>
                <pre>
                unit watch;

                interface
                uses
                Classes, windows, dialogs, SysUtils;
                const
                FILE_LIST_DIRECTORY = $0001;
                type
                PFILE_NOTIFY_INFORMATION = ^FILE_NOTIFY_INFORMATION;
                FILE_NOTIFY_INFORMATION = packed record NextEntryOffset: DWord;
                Action: DWord;
                FileNameLength: DWord;
                FileName: WideChar;
                end;
                type
                TWatch = class(TThread)
                private
                hDir: THandle;
                Watchpath: string;
                pBuffer: PFILE_NOTIFY_INFORMATION;
                dwBufSize: DWord;
                dwBytesReturned: DWord;
                pWorkPtr: PFILE_NOTIFY_INFORMATION;
                protected
                procedure Execute; override;
                procedure RefreshListbox;
                destructor Destroy;
                public
                constructor Create(path: string);
                end;
                implementation
                uses main;

                constructor TWatch.Create(path: string);
                begin
                inherited Create(False);
                watchpath := path;
                FreeOnTerminate := True;
                dwBufSize := 1024;
                dwBytesReturned := 0;
                GetMem(pBuffer, dwBufSize);
                pWorkPtr := pBuffer;
                hDir := CreateFile(PChar(watchpath), // pointer to the file name
                FILE_LIST_DIRECTORY, // access (read/write) mode
                FILE_SHARE_READ or FILE_SHARE_DELETE, // share mode
                nil, // security descriptor
                OPEN_EXISTING, // how to create
                FILE_FLAG_BACKUP_SEMANTICS, // file attributes
                0 // file with attributes to copy
                );
                end;

                procedure TWatch.Execute;
                begin
                while True do
                begin // normalerweise eine Endlosschleife
                ReadDirectoryChangesW(hDir,
                pWorkPtr,
                dwBufSize,
                true,
                FILE_NOTIFY_CHANGE_FILE_NAME {+
                FILE_NOTIFY_CHANGE_LAST_WRITE +
                FILE_NOTIFY_CHANGE_DIR_NAME +
                FILE_NOTIFY_CHANGE_ATTRIBUTES +
                FILE_NOTIFY_CHANGE_SIZE +
                FILE_NOTIFY_CHANGE_LAST_ACCESS +
                FILE_NOTIFY_CHANGE_CREATION +
                FILE_NOTIFY_CHANGE_SECURITY},
                @dwBytesReturned, nil, nil);
                synchronize(RefreshListbox);
                if Terminated then break;
                end;
                end;

                procedure TWatch.RefreshListbox;
                // hier bitte anpassen !!!! // Irgendeine Aktion Auslösen etc.
                begin
                mainw.filebox.Items.Add(WideCharLenToString(@pWork Ptr.FileName, pWorkPtr.FileNameLength shr 1));
                mainw.filebox.update;
                mainw.Caption := inttostr(mainw.filebox.Items.count);
                //mainw.statusbar.Panels[1].text:= 'Aktueller Eingang: ' + inttostr (Form1.filebox.Items.count)
                end;

                destructor TWatch.Destroy;
                begin
                if hDir <> 0 then
                FindCloseChangeNotification
                (hDir);
                FreeMem(pBuffer, dwBufSize);
                CloseHandle(hDir);
                inherited Destroy;
                end;
                end.
                </pre&gt

                Comment


                • #9
                  Hallo,

                  funktioniert das ganze auch mit DOS-Eingabeaufforderung

                  Comment


                  • #10
                    Du meinst, wenn man von der DOS-Eingabeaufforderung Dateien kopiert ? J

                    Comment


                    • #11
                      Hallo Leute,

                      ein kurzer Hinweis zu den beiden Listings (Postings 6 und 7):

                      Die Variable "FileBox" ist vom Typ "TListBox" und ist in Delphi zu finden über den Reiter "Standard".

                      Noch ein kurzer Kommentar zu der folgenden Quelltextzeile:
                      <PRE>
                      Form1.statusbar.Panels[1].text:= 'Aktueller Eingang: ' + inttostr (Form1.filebox.Items.count)
                      </PRE>

                      Damit es zur Laufzeit zu keinem "EListError" kommt, ist es notwendig, in der Delphi-IDE bei der StatusBar 2 Bereiche zu definieren (mit der rechten Maustaste auf Bereichseditor klicken).

                      Das Listing funktioniert schon ganz gut. Was mich jetzt noch interessieren würde.

                      1. Wie kann ich mir die letzte Uhrzeit der Änderung anzeigen lassen (am besten ebenfalls in der FileBox) ?

                      2. Wie kann ich mir anzeigen lassen, von welchem Benutzer bzw. von welchem PC aus die Änderung erfolgt ist ?

                      Grüsse,
                      Carsten

                      P.S. Vielen Dank an die beiden Quellcode-Poster Bernd und Frank. Dies erspart einem wirklich Arbeit

                      Comment


                      • #12
                        Hallo Leute,

                        habe gerade noch etwas mit dem Watch-Programm herumgespielt. Wenn ich "C:\Temp" als Watch-Verzeichnis nehme, so protokolliert er beim Löschen und Anlegen einer Datei den Filenamen in der Listbox.

                        Stelle ich aber das zu untersuchende Verzeichnis um auf ein NTFS-Laufwerk, so merkt der Watcher, daß ich eine Datei in dem betreffenden Ordner neu anlege. Jedoch wird eine Löschung der Datei nicht immer bemerkt. Was kann das wieder für Ursachen haben ?

                        Grüsse,
                        Carste

                        Comment


                        • #13
                          Hallo Leute,

                          den Quellcode aus Posting 7 verstehe ich ehrlich gesagt noch nicht so ganz. Kann ihn vielleicht ein Experte erklären ?

                          Besonders interessiert mich die Quelltextzeile in der Prozedur "TWatch.RefreshListbox":
                          <PRE>
                          mainw.filebox.Items.Add(WideCharLenToString(@pWork Ptr.FileName, pWorkPtr.FileNameLength shr 1));
                          mainw.filebox.update;
                          mainw.Caption := inttostr(mainw.filebox.Items.count);
                          </PRE>

                          Wie schaffe ich es, noch mehr Attribute mit in die ListBox auszugeben (z.B.
                          <PRE>
                          FILE_NOTIFY_CHANGE_LAST_WRITE
                          FILE_NOTIFY_CHANGE_DIR_NAME
                          FILE_NOTIFY_CHANGE_ATTRIBUTES
                          FILE_NOTIFY_CHANGE_SIZE
                          FILE_NOTIFY_CHANGE_LAST_ACCESS
                          FILE_NOTIFY_CHANGE_CREATION
                          FILE_NOTIFY_CHANGE_SECURITY
                          </PRE>

                          Muß ich dazu noch an anderer Stelle im Quellcode etwas ändern ?

                          In welchem Buch ist soetwas gut erklärt ?
                          Hoffe, es gibt noch weitere Tips für dieses Problem.

                          Grüsse,
                          Carste

                          Comment


                          • #14
                            @Bernd, ich hatte diesen hier im Forum schonmal gepostet <br>
                            @Carsten, ja du musst in TWatch.Execute die Flags bei ReadDirectoryChangesW() definieren. Momentan ist dort nur ein Flag aktiviert.

                            Aber Vorsicht! um so mehr Flags aktiviert sind um so häufiger stellen sich Ereignisse ein. Nach meinen Erfahrungen kann man sich auf ReadDirectoryChangesW() nicht 100%tig verlassen. Es gibt immer wieder Möglichkeiten an Dateien/Ordnern rumzuwerkeln ohne das man was davon mitbekommt. Besonders wenn massive Änderungen vorkommen scheint es so zu sein als ob ReadDirectoryChangesW() Events verschluckt.

                            Gruß Hage

                            Comment


                            • #15
                              Hi Leute,

                              es werden keine Events verschluckt, oder Dateiänderungen nicht gesehen.
                              In der Struktur FILE_NOTIFY_INFORMATION sind unter umständen mehrere Einträge enthalten.
                              Damit alle erfasst werden muss in die oben genannten Prozedur "RefreshListbox" wie folgt geändert werden.

                              <PRE>procedure TWatch.RefreshListbox;
                              var fin: Boolean;
                              begin
                              repeat
                              // irgendwas
                              mainw.filebox.Items.Add(WideCharLenToString(@pWork Ptr.FileName, pWorkPtr.FileNameLength shr 1));
                              mainw.filebox.update;
                              mainw.Caption := inttostr(mainw.filebox.Items.count);
                              // ende irgendwas
                              fin := pWorkPtr.NextEntryOffset = 0;
                              pWorkPtr := PFILE_NOTIFY_INFORMATION(DWORD(pBuffer) + pWorkPtr.NextEntryOffset);
                              until fin;
                              end;
                              </PRE>

                              Gruß Thorbe

                              Comment

                              Working...
                              X