Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
prog:lazarus:cas:files:watch [07/05/2021 10:53]
thierry [Comment ça marche ?]
prog:lazarus:cas:files:watch [09/05/2021 15:18] (Version actuelle)
thierry [Exemple de code]
Ligne 8: Ligne 8:
 Pour faire simple : Pour faire simple :
  
-On récupere les Handle des répértoires a surveiller avec+On récupere les Handle des répértoires a surveiller avec [[https://​docs.microsoft.com/​en-us/​windows/​win32/​api/​fileapi/​nf-fileapi-findfirstchangenotificationa|FindFirstChangeNotification]]
 <code delphi> <code delphi>
 function FindFirstChangeNotification(lpPathName:​LPCSTR;​ bWatchSubtree:​WINBOOL;​ dwNotifyFilter:​DWORD):​HANDLE;​ function FindFirstChangeNotification(lpPathName:​LPCSTR;​ bWatchSubtree:​WINBOOL;​ dwNotifyFilter:​DWORD):​HANDLE;​
Ligne 21: Ligne 21:
 </​code>​ </​code>​
  
-Ensuite on attends une modification avec :+Ensuite on attends une modification avec [[https://​docs.microsoft.com/​fr-fr/​windows/​win32/​api/​synchapi/​nf-synchapi-waitformultipleobjects|WaitForMultipleObjects]]:
 <code delphi> <code delphi>
 function WaitForMultipleObjects( nCount:​DWORD;​ lpHandles : PWOHandleArray;​ bWaitAll:​WINBOOL;​ dwMilliseconds:​DWORD):​DWORD function WaitForMultipleObjects( nCount:​DWORD;​ lpHandles : PWOHandleArray;​ bWaitAll:​WINBOOL;​ dwMilliseconds:​DWORD):​DWORD
 </​code>​ </​code>​
   * ''​nCount''​ = nombre de handle dans la liste   * ''​nCount''​ = nombre de handle dans la liste
-  * ''​lpHandles''​ = pointer vers le premier enregistrement de la liste (''​@HandleList[0]''​)+  * ''​lpHandles''​ = pointer vers le premier enregistrement de la liste de type ''​THandleList'' ​(voir ci-dessus) ​ (dans notre cas=''​@HandleList[0]''​)
   * ''​bWaitAll''​ = Boolean -> On attend toutes les modifs ??? (''​False''​ dans notre cas)   * ''​bWaitAll''​ = Boolean -> On attend toutes les modifs ??? (''​False''​ dans notre cas)
   * ''​dwMilliseconds''​ = Temps d'​attente en Millisecondes   * ''​dwMilliseconds''​ = Temps d'​attente en Millisecondes
  
 +La fonction ''​ WaitForMultipleObjects''​ retourne un DWord qui peut être interprété de la façon suivante:
 +  * $102 (258) = TIMEOUT, le temps d'​attente (''​dwMilliseconds''​) s'est écoulé.
 +  * $FFFFFFFF (-1) = FAILED !
 +  * $0..$40 = Index (+WAIT_OBJECT_0) du Handle dans la liste correspondant au répertoire qui a été modifié
 +  * $80..$C0 = Index (+WAIT_ABANDONED_0) du Handle qui... je sais pas trop
  
-==== Titre ====+Plus d'​infos -> [[https://​docs.microsoft.com/​fr-fr/​windows/​win32/​api/​synchapi/​nf-synchapi-waitformultipleobjects#​return-value|Valeur de retour de la fonction WaitForMultipleObjects]] 
 + 
 +On traite la valeur retournée par la fonction ''​ WaitForMultipleObjects''​.\\ 
 +Puis on retourne a la surveillance par un [[https://​docs.microsoft.com/​en-us/​windows/​win32/​api/​fileapi/​nf-fileapi-findnextchangenotification| FindNextChangeNotification]] et rebouclage sur  [[https://​docs.microsoft.com/​fr-fr/​windows/​win32/​api/​synchapi/​nf-synchapi-waitformultipleobjects|WaitForMultipleObjects]]:​ 
 +<code delphi>​ 
 +function FindNextChangeNotification(hChangeHandle:​HANDLE):​WINBOOL;​ 
 +</​code>​ 
 +ou le parametre ''​hChangeHandle''​ est le Handle du répertoire modifié precedement.\\ 
 +Ce Handle est récuperable dans la liste des Handles THandleList passé a la fonction ''​WaitForMultipleObjects''​ a l'​index retourné par cette méme fonction... (suis-je bien claire ???)  
 + 
 +Exemple de code (Dans un Thread, car ''​WaitForMultipleObjects''​ est une fonction bloquante...) : 
 +<code delphi>​ 
 +   // vIndex: integer; 
 +   // vHandleList : THandleList (voir ci-dessus) 
 +   // FCount ​nombre de handle dans la liste vHandleList 
 +   // FWaitMs ​nombre de millisecondes a attendre 
 +   // FDirChangedHandle ​Handle du Find Change Notifier du répertoire qui a été modifié 
 +   // Le Handle du Find Change Notifier est retourné par la function FindFirstChangeNotification (voir ci-dessus) 
 +         while not Terminated do 
 +           ​begin 
 +            vIndex :WaitForMultipleObjects(FCount,​ vHandleList[0],​ False, FWaitMs); 
 +            if (vIndex >WAIT_OBJECT_0) and 
 +               ​(vIndex < WAIT_OBJECT_0 + FNHandleList.Count) ​ then 
 +              begin 
 +                  FDirChangedHandle :=FHandleList[vIndex - WAIT_OBJECT_0];​ 
 +                  ... 
 +                  FindNextChangeNotification( FDirChangedHandle);​ 
 +              end; 
 +           ​end;​ 
 +</​code>​ 
 +==== ReadDirectoryChangesW ==== 
 + 
 +=== Exemple de code === 
 + 
 +Voici un exemple de code pour l'​utilisation de ''​ReadDirectoryChangesW'':​ 
 + 
 +Documentation utile pour mieux comprendre ce code : 
 +  * [[prog:​lazarus:​cas:​files:​createfile|Documentation sur la function CreateFile]] 
 +  * [[https://​docs.microsoft.com/​en-us/​windows/​win32/​api/​winbase/​nf-winbase-readdirectorychangesw|Documentation sur la function ReadDirectoryChangesW]] 
 +  * [[https://​docs.microsoft.com/​en-us/​windows/​win32/​api/​ioapiset/​nf-ioapiset-getoverlappedresult|Documentation sur la function GetOverlappedResult]] 
 +  * [[https://​docs.microsoft.com/​en-us/​windows/​win32/​api/​winnt/​ns-winnt-file_notify_information|Documentation sur le type FILE_NOTIFY_INFORMATION]] 
 +<code delphi DirMonDOS.lpr>​ 
 +program DirMonDOS;​ 
 + 
 +{$mode objfpc}{$H+} 
 + 
 +uses 
 + ​{$IFDEF UNIX} 
 + ​{$IFDEF UseCThreads} 
 +   ​cthreads,​ 
 + ​{$ENDIF} 
 + ​{$ENDIF} 
 +   ​Classes,​ 
 +   ​crt,​ 
 +   ​JwaWindows,​ 
 +   ​SysUtils;​ 
 + 
 +var 
 +   ​FPathName:​ string; 
 +   ​FDirHandle:​ THandle; 
 +   ​FBuffer: ​ array[0..9999] of byte; 
 +   ​FBytesReturned:​ dword; 
 +   ​FOverLapped:​ TOverlapped;​ 
 +   ​FOverResult:​ boolean; 
 +   ​FFileNotifyInformation:​ PFileNotifyInformation;​ 
 +   ​FOffset: ​ dword; 
 +   ​FAction: ​ dword; 
 +   ​FActionStr:​ string; 
 +   ​FFilename:​ string; 
 +   ​FLen: ​    ​dword;​ 
 +   ​FTerminate:​ boolean; 
 +   ​FLastError:​ dword; 
 +   ​FNotifyFilter:​ DWord; 
 +   ​FLooping:​ boolean; 
 + 
 +begin 
 +   { Répertoire à surveiller } 
 +   ​FPathName ​    := '​J:​\';​ 
 + 
 +   { Initialisation des variables } 
 +   ​FTerminate := False; 
 +   ​FLooping ​  := False; 
 +   ​ZeroMemory(@FBuffer,​ sizeof(FBuffer));​ 
 +   ​FNotifyFilter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or 
 +      FILE_NOTIFY_CHANGE_LAST_WRITE;​ 
 +   { FILE_NOTIFY_CHANGE_FILE_NAME = 1; 
 +     ​FILE_NOTIFY_CHANGE_DIR_NAME = 2; 
 +     ​FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; 
 +     ​FILE_NOTIFY_CHANGE_SIZE = 8; 
 +     ​FILE_NOTIFY_CHANGE_LAST_WRITE = 16; 
 +     ​FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; 
 +     ​FILE_NOTIFY_CHANGE_CREATION = 64; 
 +     ​FILE_NOTIFY_CHANGE_SECURITY = 256; } 
 + 
 +   ​writeln('​Press [s] to exit'​);​ 
 +   ​WriteLn('​Surveillance du dossier [', FPathName, '​]'​);​ 
 + 
 +   { Récuperation du Handle du Répertoire a surveiller } 
 +   ​FDirHandle := CreateFile(PChar(FPathName),​ FILE_LIST_DIRECTORY,​ 
 +      FILE_SHARE_READ or FILE_SHARE_DELETE or FILE_SHARE_WRITE,​ nil, 
 +      OPEN_EXISTING,​ FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,​ 0); 
 +   ​WriteLn('​Handle du dossier [', FDirHandle, '​]'​);​ 
 + 
 + 
 +   if FDirHandle <> INVALID_HANDLE_VALUE then 
 +     ​begin 
 +        try 
 +          { Bouclage jusqu'​a ce que Terminate=True } 
 +          repeat 
 +             { Mise en place de la surveillance } 
 +             if ReadDirectoryChangesW(FDirHandle,​ @FBuffer, sizeOf(FBuffer),​ 
 +               True, FNotifyFilter,​ @FBytesReturned,​ @FOverLapped,​ nil) then 
 +              begin 
 + 
 +               { Bouclage jusqu'​a ce que FOverResult=True } 
 +               ​repeat 
 +                  { Si FOverResult = True, cela signifie qu'il y a eu des modifications } 
 +                  FOverResult := 
 +                     ​GetOverlappedResult(FDirHandle,​ FOverLapped,​ 
 +                     ​FBytesReturned,​ False); 
 +                  { Je capture GetLastError ci-dessous, car il peut etre modifié 
 +                    par d'​autres instructions,​ comme un WriteLn par exemple } 
 +                  FLastError ​ := GetLastError;​ 
 + 
 +                  { Affichage des informations pour faire beau } 
 +                  if not FOverResult and not FLooping then 
 +                    begin 
 +                     ​Writeln('​Looping...'​);​ 
 +                     ​FLooping := True; 
 +                    end; 
 +                  sleep(100);​ 
 +                  { Verification si on a pressé la touche [S] pour arreter le processus } 
 +                  if KeyPressed and (ReadKey = '​s'​) then 
 +                     ​FTerminate := True; 
 + 
 +               until FOverResult or (FLastError <> ERROR_IO_INCOMPLETE) or FTerminate;​ 
 +               ​FLooping := False; 
 + 
 +               if FOverResult then 
 +                 ​begin 
 +                  Writeln('​--- begin modification ---'​);​ 
 + 
 +                  { Positionnement sur le debut du Buffer } 
 +                  pointer(FFileNotifyInformation) := @FBuffer[0];​ 
 +                  repeat 
 +                     ​FOffset := FFileNotifyInformation^.NextEntryOffset;​ 
 +                     ​FAction := FFileNotifyInformation^.Action;​ 
 +                     { FLen = FileNameLength/​2 
 +                       car FileName est codé en WideString 
 +                       et WideString est 2x plus long que AnsiString 
 +                       ​->​https://​wiki.freepascal.org/​Character_and_string_types/​fr } 
 +                     ​FLen ​   := FFileNotifyInformation^.FileNameLength div 2; 
 +                     ​WideCharLenToStrVar(@(FFileNotifyInformation^.FileName),​ 
 +                        FLen, FFilename);​ 
 + 
 +                     case FAction of 
 +                        FILE_ACTION_ADDED:​ FActionStr ​   := '​Created';​ 
 +                        FILE_ACTION_REMOVED:​ FActionStr ​ := '​Deleted';​ 
 +                        FILE_ACTION_MODIFIED:​ FActionStr := '​Modified';​ 
 +                        FILE_ACTION_RENAMED_OLD_NAME:​ FActionStr := '​RenamedFrom';​ 
 +                        FILE_ACTION_RENAMED_NEW_NAME:​ FActionStr := '​RenamedTo';​ 
 +                        else 
 +                           ​FActionStr := '???';​ 
 +                       ​end;​ 
 + 
 +                     { Affichage du résultat } 
 +                     ​WriteLn('​Action [', FAction, '​|',​ FActionStr, '] len[',​ 
 +                        FLen, '] sur fichier [', FFilename, '​]'​);​ 
 + 
 +                     { Positionnement sur le prochain FFileNotifyInformation } 
 +                     ​pointer(FFileNotifyInformation) := 
 +                        pointer(FFileNotifyInformation) + FOffset; 
 + 
 +                  until FOffset = 0; 
 +                  { Nettoyage du Buffer pour eviter d'​accumuler des parasites 
 +                    surtout dans Filename } 
 +                  ZeroMemory(@FBuffer,​ sizeof(FBuffer));​ 
 +                  Writeln('​--- end modification ---'​);​ 
 +                 ​end;​ 
 +              end; 
 +         until FTerminate;​ 
 +        finally 
 +         ​CloseHandle(FDirHandle);​ 
 +        end; 
 +     end 
 +   ​else 
 +      Writeln('​Dir Handle invalid'​);​ 
 +   ​Writeln('​Press to exit'​);​ 
 +   ​Readln;​ 
 + 
 +end. 
 +</​code>​ 
 +=== Ressources pour ReadDirectoryChangesW === 
 +  * [[https://​www.osnews.com/​story/​7376/​a-directory-monitor-class-for-delphi/​]] 
 +  * [[https://​www.experts-exchange.com/​questions/​24440582/​Using-ReadDirectoryChangesW-in-Delphi.html]] 
 +  * [[https://​nono40.developpez.com/​sources/​source0045/​]]
  
  
  
 ==== A la Procmon ==== ==== A la Procmon ====
-[[softs:​procmon|Procmon]] est un excellent utilitaire qui montre en temps réel les accès aux fichiers par les processus.\\+[[softs:​sysinternals:​procmon|Procmon]] est un excellent utilitaire qui montre en temps réel les accès aux fichiers par les processus.\\
 Reste a savoir comment il fait ? Reste a savoir comment il fait ?