Ceci est une ancienne révision du document !
Surveiller modification Fichier/Répertoire
Pistes de recherches
FindFirstChangeNotification
Cette méthode est documentée ici → Obtention de notifications de modification de répertoire
Apparemment cette méthode permet de savoir si un dossier a été modifier, mais pas de connaitre les modification apportées. Quel fichier modifié ? Quel fichier ajouté ou supprimé ?
Comment ça marche ?
Pour faire simple :
On récupere les Handle des répértoires a surveiller avec FindFirstChangeNotification
function FindFirstChangeNotification(lpPathName:LPCSTR; bWatchSubtree:WINBOOL; dwNotifyFilter:DWORD):HANDLE;
lpPathname
= PChar('C:\MonRepertoire\Etc\')bWatchSubtree
= boolean → on surveille juste le répertoire (False) ou le répertoire et ses enfants ? (True)dwNotifyFilter
= dWord → Changement que l'on veut capturer (Filename, Dirname, Attributes, Size…) plus d'infos ici
On met ces Handle dans un array de ce type : (par exemple)
THandleList = array [0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;
Ensuite on attends une modification avec WaitForMultipleObjects:
function WaitForMultipleObjects( nCount:DWORD; lpHandles : PWOHandleArray; bWaitAll:WINBOOL; dwMilliseconds:DWORD):DWORD
nCount
= nombre de handle dans la listelpHandles
= pointer vers le premier enregistrement de la liste de typeTHandleList
(voir ci-dessus) (dans notre cas=@HandleList[0]
)bWaitAll
= Boolean → On attend toutes les modifs ??? (False
dans notre cas)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
Plus d'infos → 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 FindNextChangeNotification et rebouclage sur WaitForMultipleObjects:
function FindNextChangeNotification(hChangeHandle:HANDLE):WINBOOL;
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…) :
// 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;
ReadDirectoryChangesW
Exemple de code
Voici un exemple de code pour l'utilisation de ReadDirectoryChangesW
:
Documentation utile pour mieux comprendre ce code :
- 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.
A la 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 ?
Apparemment cette solution impliquerai l'utilisation d'un drivers annexe, ce qui reste relativement fastidieux…
Piste OpenFilesView de Nirsoft
Une piste peut etre sur la page→https://www.nirsoft.net/utils/opened_files_view.html.
Un programme de Nirsoft apparement similaire a Procmon qui fonctionne de la facon suivante:
OpenedFilesView utilise l'API NtQuerySystemInformation pour énumérer tous les handles dans le système.
Après avoir filtré les handles non-fichiers, il utilise un driver temporaire - NirSoftOpenedFilesDriver.sys pour lire les informations sur chaque handle depuis la mémoire du noyau.
Ce driver est automatiquement déchargé du système lorsque vous quittez l'utilitaire OpenedFilesView.
Projet de ShareVB
Sur cette page : Savoir quel fichier est utilisé par telle application : la liste des fichiers ouverts sur le système (pour 2k/xp/2k3) ShareVB partage un projet en VB qui ferait apparemment le taf… a étudier.
Je met ce projet sur mon site, on sait jamais dés fois qu'il disparaisse !!! 36281-1110458-savoir-quel-fichier-est-utilise-par-telle-application-la-liste-des-fichiers-ouverts-sur-le-systeme-pour-2k-xp-2k3.zip
En vrac à étudier
Sources & Ressources
- Voir aussi → JvChangeNotify.TJvChangeNotify class in JCL.
- Voir aussi → Travailler avec les Disques