Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Verzeichnisüberwachung (https://www.delphipraxis.net/51716-verzeichnisueberwachung.html)

luckystar85 17. Aug 2005 15:45


Verzeichnisüberwachung
 
Moin,

ich habe mir mal ein Programm geschrieben was ein vom benutzer eingestelltes Verzeichnis überwacht und bei Änderungen darin überprüft ob alle Dateien darin i.O. sind. Das wird mit Hilfe eines Threads gemacht.

Delphi-Quellcode:
procedure TcsDirThread.Execute;
var
  pBuf        : Pointer;
  dwBufLen    : DWORD;
  dwRead      : DWORD;
  FNI         : FILE_NOTIFY_INFORMATION;
  pWork       : Pointer;
  sFileName   : Widestring;
  dwWaitStatus : DWORD;
begin
  FhFile   := CreateFile(PChar(FsDirPath),
                          FILE_LIST_DIRECTORY or GENERIC_READ,
                          FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, nil,
                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,0);
                         
  hNotifity := FindFirstChangeNotification(PChar(FsDirPath),                   //Verzeichnis
                                           false,                              //unterverzeichnisse überwachen
                                           FILE_NOTIFY_CHANGE_FILE_NAME or
                                           FILE_NOTIFY_CHANGE_LAST_WRITE or
                                           FILE_NOTIFY_CHANGE_SIZE or
                                           FILE_ACTION_ADDED or
                                           FILE_ACTION_REMOVED or
                                           FILE_ACTION_MODIFIED);
  if (FhFile = INVALID_HANDLE_VALUE) or (FhFile = 0) then
  begin
    RaiseLastWin32Error;
    Terminate;
  end;
  if (hNotifity = INVALID_HANDLE_VALUE) then
  begin
    RaiseLastWin32Error;
    Terminate;
  end;

  dwBufLen := 65536;
  pBuf    := AllocMem(dwBufLen);
  try
    while ((FindNextChangeNotification(hNotifity)) and (not terminated)) do
    begin
      Synchronize(Application.ProcessMessages);
      dwWaitStatus := WaitForSingleObject(hNotifity, 1000);
      if (dwWaitStatus = WAIT_FAILED) then
      begin
        RaiseLastWin32Error;
        Terminate;
      end;
      if (dwWaitStatus = WAIT_OBJECT_0) then
      begin
        ReadDirectoryChangesW(FhFile,pBuf,dwBufLen,true,
                              FILE_NOTIFY_CHANGE_FILE_NAME or
                              FILE_NOTIFY_CHANGE_DIR_NAME or
                              FILE_NOTIFY_CHANGE_ATTRIBUTES or
                              FILE_NOTIFY_CHANGE_SIZE or
                              FILE_NOTIFY_CHANGE_LAST_WRITE or
                              FILE_NOTIFY_CHANGE_CREATION or
                              FILE_ACTION_ADDED or
                              FILE_ACTION_REMOVED or
                              FILE_ACTION_MODIFIED,
                              @dwRead,nil,nil);
        pWork := pBuf;
        repeat
          StrMove(@FNI,pWork,12);
          PChar(pWork) := PChar(pWork)+12;
          sFileName   := StringOfChar(#00,FNI.dwFileNameLength);
          StrMove(@sFileName[1],pWork,FNI.dwFileNameLength);
          FsFileName := WideCharToString(PWideChar(sFileName));
          FsFileName := copy(FsFileName,1,length(FsFileName) shl 1);
          FsReason  := GetReason(FNI.dwAction);
          Synchronize(AddFileToList);
          PChar(pWork) := PChar(pBuf)+FNI.dwNextEntryOffset;
        until FNI.dwNextEntryOffset = 0;
      end;
    end;

    if (LowerCase(ExtractFileExt(FsFileName)) = '.bmp') then
    begin
      PostMessage(MainForm.Handle, Verzeichnisaenderung_bmp, 0, ProjektID);
    end;
  finally
    FreeMem(pBuf,dwBufLen);
  end;
end;
Jetzt habe ich mal eine neuen Datei angelegt und wieder gelöscht dann ist das alles kein Problem. Die Änderungen wird mit AddFileToList in eine TStringList eingefügt und kann bei bedarf angezeigt ode gespeichert werden. Anschließend habe ich mal mit TotalCommander das Dateidatum geändert und wollte mir dann mal die StringList angucken. Da trat mein Problem auf: In meiner StringList waren fast 500000 einträge. Das Speichern der Datei war möglich, dauerte natürlich sehr lange und die Datei war anchher 142 MB groß (reine Textdatei), aber das Anzeigen der StringList in TListBox mit Assign ging gar nicht mehr. Nachdem die Auslagerungsdatei auf 1,94 GB erweitert wurde, wollte mein Rechner nicht mehr.
Warum sind da sol viele Änderungen drin. Das lustige dabei ist das bis auf die ersten 2 Änderungen (Datei anlegen und löschen) immer das selbst stand (Datei wurde geändert). Immer wieder das selbe. Im Abstand von weniger als 1 sekunde. Ist das jetzt ein Problem bei mir oder ein Problem von Windows das ich umgehen muss? Wäre nett wenn ihr mir Tipps und Anregungen geben würdet.

Olli 17. Aug 2005 16:49

Re: Verzeichnisüberwachung
 
Was genau der Total Commander macht, ist fraglich ... was hast du denn unternommen um das Dateidatum zu ändern? Klingt für mich danach, als würde "einfach" die Datei kopiert oder neu angelegt o.ä.

Es gibt zB auch Editoren die von der zu bearbeitenden Datei eine Kopie anlegen. Das kann durchaus ein Problem sein im Falle von Hardlinks! Denn dann wird der Hardlink zerbrochen und "die Datei" existiert wieder als "zwei verschiedene Dateien".

An deiner Stelle würde ich den Code so ändern, daß ich alle Aktionen auf eine Datei dann eben zusammenfasse, bis sich der Dateiname ändert ... sozusagen "stateful" machen. Ansonsten wirst du wohl damit leben müssen. Du kannst ja mal gern den FileMon von Sysinternals testen, aber das Ergebnis wird sehr ähnlich sein.

luckystar85 17. Aug 2005 16:55

Re: Verzeichnisüberwachung
 
Ich habe die Datei in TotalCommander markiert bin dann im Menü "Datei" auf "Dateiattribute ändern" gegangen und habe dort "Datum/Zeit" auf die aktuelle Zeit gesetzt.
Ich könnte zwar alle Ergebnisse dieser einen Datei zu einem zusammenfassen, aber es kann ja sein das sich die Datei heute ändert und morgen auch noch mal. Dazu ist das Protokoll ja da, um das zu protokollieren. Das Programm soll auf einem Server laufen und alle Dateien überprüfen ob die noch korrekt sind, wenn eine Änderung drinne war. Und fast 500000 mal den Test starten ob die Dateien noch korrekt sind ist auch doof, das bringt den Rechner an den Rand des Absturtzes. Vor allem da nur eine Änderung gemacht wurde.

Olli 17. Aug 2005 17:09

Re: Verzeichnisüberwachung
 
Aber "noch korrekt" bezieht sich doch sicherlich auf den Inhalt. Überlege mal was du dort alles für Änderungen überwachst, das allein ist doch schon irre auf einem Server! Stattdessen sollte sich der Thread schlafenlegen (zB 10s), wenn er eine Änderung bemerkt hat und entsprechende Maßnahmen getroffen hat. Außerdem sollten nur relevante Änderungen überwacht und dann vorzugsweise nochmal von dir gefiltert werden.

luckystar85 17. Aug 2005 17:21

Re: Verzeichnisüberwachung
 
Kann ich einen Thread auch einfach mit Sleep pausieren?
Ich dachte das wäre so die wichtigsten Änderungen:

FILE_NOTIFY_CHANGE_FILE_NAME - Datiename hat sich geändert
FILE_NOTIFY_CHANGE_DIR_NAME - Verzeichnisname hat sich geändert
FILE_NOTIFY_CHANGE_ATTRIBUTES - Dateiattribut hat sich geändert
FILE_NOTIFY_CHANGE_SIZE - Dateigröße hat sich geändert
FILE_NOTIFY_CHANGE_LAST_WRITE - Datei gespeichert
FILE_NOTIFY_CHANGE_CREATION - Datei ersteltl
FILE_ACTION_ADDED - Date ist hinzugekommen
FILE_ACTION_REMOVED - Datei wurde gelöscht
FILE_ACTION_MODIFIED - Datei wurde geändert

Ok, ich denke das Größe, Attribut ändern brauch ich nicht zu überwachen und FILE_NOTIFY_CHANGE_CREATION werde ich auch rausnehmen. Der Rest erscheint mir eigentlich sehr sinnvoll.

Olli 17. Aug 2005 18:57

Re: Verzeichnisüberwachung
 
Zitat:

Zitat von luckystar85
Kann ich einen Thread auch einfach mit Sleep pausieren?

Natürlich. Das ist exakt was Sleep() nunmal macht. ZB Sleep(0) reicht einfach nur die Rechenzeit an den nächsten Thread weiter. Das dient z.B. bei einer engen Schleife dazu, daß die CPU-Last sich in Grenzen hält.

Zitat:

Zitat von luckystar85
Ok, ich denke das Größe, Attribut ändern brauch ich nicht zu überwachen und FILE_NOTIFY_CHANGE_CREATION werde ich auch rausnehmen. Der Rest erscheint mir eigentlich sehr sinnvoll.

Formulier es doch mal andersherum. Was ist die Zielstellung? Poste diese hier - wohlgemerkt, die Zielstellung und nicht deinen Ansatz zur Lösung. Vielleicht gibt es was ganz normales. Vielleicht kann man auch einfach bestimmte Dinge weglassen bei den Überwachungen.

luckystar85 17. Aug 2005 20:17

Re: Verzeichnisüberwachung
 
Mein Ziel:
Auf einem Server werden in unregelmäßigen Abständen Dateien kopiert oder ersetzt. Jetzt soll nach jeder Änderung überprüft werden ob die Dateien noch i.O. sind.
Das Überprüfen ist kein Problem das funzt soweit auch vollständig. Bloß diese 500000 Ändeurngen am Verzeichnis machen mir Porbleme.

Olli 17. Aug 2005 20:27

Re: Verzeichnisüberwachung
 
Okay, wodurch bestimmt sich "i. O."??? Durch den Inhalt? Änderungszeit? Attribute? Größe?

luckystar85 17. Aug 2005 20:28

Re: Verzeichnisüberwachung
 
Das wird anhand des Inhaltes festegelegt, das heißt in welchen speziellen Format die Dateien vorliegen (bei Bmp's farbtiefe, etc.)

Olli 17. Aug 2005 20:49

Re: Verzeichnisüberwachung
 
Okay, dann gehen wir mal die Flags durch:
  • Löschen, Erstellen oder Umbenennen:
    FILE_NOTIFY_CHANGE_FILE_NAME (Changes include renaming, creating, or deleting a file)
  • Löschen oder Erstellen oder Umbenennen eines Verzeichnisses:
    FILE_NOTIFY_CHANGE_DIR_NAME (Changes include creating or deleting a directory)
  • Attribute geändert:
    FILE_NOTIFY_CHANGE_ATTRIBUTES (Any attribute change in the watched directory or subtree causes a change notification wait operation to return)
  • Größe ändert sich:
    FILE_NOTIFY_CHANGE_SIZE (Any file-size change in the watched directory or subtree causes a change notification wait operation to return. The operating system detects a change in file size only when the file is written to the disk. For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed)
  • Dateizeit Letzte Änderung:
    FILE_NOTIFY_CHANGE_LAST_WRITE (Any change to the last write-time of files in the watched directory or subtree causes a change notification wait operation to return. The operating system detects a change to the last write-time only when the file is written to the disk. For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed)
  • Dateizeit Letzter Zugriff (auch Lesen):
    FILE_NOTIFY_CHANGE_LAST_ACCESS (Any change to the last access time of files in the watched directory or subtree causes a change notification wait operation to return)
  • Dateizeit Erstellungsdatum ändert sich:
    FILE_NOTIFY_CHANGE_CREATION (Any change to the creation time of files in the watched directory or subtree causes a change notification wait operation to return)
Rot ist für dich interessant, grün nur wenn die Dateien auf NTFS sind. Ich würde dir dringend empfehlen, daß die Dateien auf NTFS liegen und der Dateicache großzügig eingestellt wird.

Sobald eine Änderung passiert, solltest du den Thread schlafenlegen nachdem du deine Überprüfung gemacht hast. Nach dem Aufwachen machst du den nächsten Durchgang. Allerdings verstehe ich noch nicht, wieso du FindNextChangeNotification und ReadDirectoryChangesW mischst. IMO reicht es in einem Thread, wenn du einfach die synchrone Variante von ReadDirectoryChangesW aufrufst und somit die Funktion erst zurückkehrt, wenn was passiert ist. Danach kann sich der Thread wieder schlafenlegen indem die Funktion wieder aufgerufen wird (ich empfehle davor ein Sleep(0);).

Die beiden Methoden zur Ermittlung von Änderungen müssen nicht kombiniert eingesetzt werden! FindFirst*/Next* ermittelt nur das OB, ReadDirectory* ermittelt das WAS! Das ist der Unterschied. Ich empfehle letztere Methode, da dies die für dich passende zu sein scheint.

luckystar85 17. Aug 2005 20:53

Re: Verzeichnisüberwachung
 
Danke für deine Hilfe, ich werde das mal so bei mir einspielen.

vt670 26. Sep 2005 12:21

Re: Verzeichnisüberwachung
 
Hallo,

ich arbeite an etwas ähnlichem. Aus bequemlichkeit jedoch mit dem TRxFolderMonitor (FindFirstChangeNotification) der zwar einwandfrei arbeitet leider aber keine Information liefert "welche" datei modifiziert wurde.
Da ReadDirectoryChangesW nicht für alle windows versionen verfügbar :cry: ist wäre ich dankbar wenn jemand eine elegante Methode kennt wie ich ermitteln kann welche Datei(en) sich geändert haben.

vielen dank für's lesen
VT

Olli 26. Sep 2005 13:40

Re: Verzeichnisüberwachung
 
Um ReadDirectoryChangesW wirst du aber nicht herumkommen, es sei denn du willst unsaubere (und vor allem unsichere) Methoden wie API-Hooking verwenden.

vt670 26. Sep 2005 13:45

Re: Verzeichnisüberwachung
 
Zitat:

Zitat von Olli
Um ReadDirectoryChangesW wirst du aber nicht herumkommen, es sei denn du willst unsaubere (und vor allem unsichere) Methoden wie API-Hooking verwenden.

Hi Olli,
danke für deine antwort.
Nein, mag keine unsaubere Methoden :zwinker:
Hab schon sowas befürchtet grr...

Eure diskussion zu dem Thema war dennoch sehr interessant.

viele grüsse
vt

Olli 26. Sep 2005 13:49

Re: Verzeichnisüberwachung
 
Gibt es wichtige Gründe weshalb du noch Windows 9x/Me unterstützen müßtest? Ich meine, bei dem Produkt, an dem ich gerade arbeite wurde der Win9x/Me-Support gekippt. Das erlaubt viel sauberere Programmierung. Auch wenn es sein kann, daß das Programm dennoch auf 9x/Me läuft, wird dieses nicht mehr explizit unterstützt.

vt670 26. Sep 2005 14:23

Re: Verzeichnisüberwachung
 
Zitat:

Zitat von Olli
Gibt es wichtige Gründe weshalb du noch Windows 9x/Me unterstützen müßtest? Ich meine, bei dem Produkt, an dem ich gerade arbeite wurde der Win9x/Me-Support gekippt. Das erlaubt viel sauberere Programmierung. Auch wenn es sein kann, daß das Programm dennoch auf 9x/Me läuft, wird dieses nicht mehr explizit unterstützt.

Hast schon Recht, aber diese Funktion ist so interessant das sie auch in Projekte angewendet werden kann die auf ältere Windows versionen laufen.

himitsu 1. Sep 2006 12:02

Re: Verzeichnisüberwachung
 
Wollte hier nur mal erwähnen daß FILE_ACTION_ADDED, FILE_ACTION_REMOVED und FILE_ACTION_MODIFIED im NotifyFilter von FindFirstChangeNotification und ReadDirectoryChanges nichst zu suchen hat.

FILE_ACTION_* sind die Ergebnisse und keine Filter :warn:


Man braucht sich dann nicht wundern, wenn z.B. die Ergebnisse für FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME und FILE_NOTIFY_CHANGE_ATTRIBUTES ebenfalls geliefert werden, selbst wenn diese "garnicht" gewollt sind (für den Grund braucht man sich nur mal die Werte der Konstanten ansehen).


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz