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 16:25]
thierry [ReadDirectoryChangesW]
prog:lazarus:cas:files:watch [09/05/2021 15:18] (Version actuelle)
thierry [Exemple de code]
Ligne 67: Ligne 67:
 </​code>​ </​code>​
 ==== ReadDirectoryChangesW ==== ==== 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.osnews.com/​story/​7376/​a-directory-monitor-class-for-delphi/​]]
   * [[https://​www.experts-exchange.com/​questions/​24440582/​Using-ReadDirectoryChangesW-in-Delphi.html]]   * [[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 ?