hudsonhock Posted August 14 Share Posted August 14 Hi, I am creating a script to monitor the changes happen in a given directory/folder. Found _WinAPI_ReadDirectoryChanges which demonstrate an accurate detection on the change (I am detecting the change of size), however, I need some help here. Basically my script does as following: use it to monitor changes for a given directory as soon as there is changes occur, it start and continue to monitor (keep looping) as soon as there is no changes detect for more than 5 second, it exit the loop #include <APIFilesConstants.au3> #include <Array.au3> #include <AutoItConstants.au3> #include <MsgBoxConstants.au3> #include <WinAPIError.au3> #include <WinAPIFiles.au3> #include <WinAPIMem.au3> Global $g_sPath = "C:\Desktop\TestProgram_SIM\" Local $hDirectory = _WinAPI_CreateFileEx($g_sPath, $OPEN_EXISTING, $FILE_LIST_DIRECTORY, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), $FILE_FLAG_BACKUP_SEMANTICS) If @error Then _WinAPI_ShowLastError('', 1) EndIf Local $pBuffer = _WinAPI_CreateBuffer(8388608) Local $aData ConsoleWrite(TimerDiff($iStartTime) & @CRLF) While TimerDiff($iStartTime) < 3000 ConsoleWrite("IN" & @CRLF) $aData = _WinAPI_ReadDirectoryChanges($hDirectory, $FILE_NOTIFY_CHANGE_SIZE, $pBuffer, 8388608, 1) If Not @error Then ;~ _ArrayDisplay($aData, '_WinAPI_ReadDirectoryChanges') ConsoleWrite("Changes Occur @Timer:" & TimerDiff($iStartTime) & @CRLF) Else ConsoleWrite("NO detection:" & @CRLF) _WinAPI_ShowLastError('', 1) EndIf Sleep(100) WEnd ConsoleWrite("OUT" & @CRLF) I am using the Example found in Helpfile, but couldn't workaround how to exit the loop after no more changes is detected, the script will just pause and wait for any changes (continuosly) after calling _WinAPI_ReadDirectoryChanges. Appreciate if anyone could shade some light here, thank you. Link to comment Share on other sites More sharing options...
ioa747 Posted August 14 Share Posted August 14 you need Local $iStartTime = TimerInit() before While TimerDiff($iStartTime) < 3000 I think I know that I know nothing Link to comment Share on other sites More sharing options...
argumentum Posted August 14 Share Posted August 14 ..for that example, you'll need to create a file in the path you're watching and use that file to trigger an exit. Say "thatSit.txt". When _WinAPI_ReadDirectoryChanges() catches that, you know that that is your cue to exit. Otherwise you'll need to ProcessClose it. Time for me to catch some ZZzzz Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
Solution Nine Posted August 14 Solution Share Posted August 14 (edited) As per help file : Quote The _WinAPI_ReadDirectoryChanges() function works only in synchronous mode. It means this is a blocking function, it will wait forever for a change. But there is a way to make it asynchronous. You would need to use the Overlapped version of it. Search the forum, I believe there is an example of it. ps. made one for the fun of it : expandcollapse popup#include <Constants.au3> #include <WinAPI.au3> Opt("MustDeclareVars", True) HotKeySet("{ESC}", Terminate) Global Const $WATCH_PATH = "C:\Apps\Temp" Global Const $BUFFER_LENGTH = 4096 Global Const $NOTIFY_FLAGS = BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME) Global $tOverlapped, $hDirectory, $hCompletion, $pCompletion, $pDataBuffer, $hEvent DirectoryChange() Func DirectoryChange() $tOverlapped = DllStructCreate($tagOVERLAPPED) $hEvent = _WinAPI_CreateEvent(0, False, False) $tOverlapped.hEvent = $hEvent $hDirectory = _WinAPI_CreateFileEx($WATCH_PATH, $OPEN_EXISTING, $FILE_LIST_DIRECTORY, _ BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE, $FILE_SHARE_DELETE), _ BitOR($FILE_FLAG_BACKUP_SEMANTICS, $FILE_FLAG_OVERLAPPED)) $hCompletion = DllCallbackRegister(CompletionRoutine, "none", "dword;dword;ptr") $pCompletion = DllCallbackGetPtr($hCompletion) $pDataBuffer = _WinAPI_CreateBuffer($BUFFER_LENGTH) _WinAPI_ReadDirectoryChangesW($hDirectory, $NOTIFY_FLAGS, $pDataBuffer, $BUFFER_LENGTH, True, $tOverlapped, $pCompletion) OnAutoItExitRegister(CleanUp) While True _WinAPI_WaitForSingleObjectEx($hEvent, 0, 1) Sleep(100) WEnd EndFunc ;==>DirectoryChange ;~ Make decisions on file changes in here Func FileNotifyUser($nFileAction, $sFileName) Switch $nFileAction Case $FILE_ACTION_ADDED ConsoleWrite("File added: " & $sFileName & @CRLF) Case $FILE_ACTION_REMOVED ConsoleWrite("File removed: " & $sFileName & @CRLF) Case $FILE_ACTION_RENAMED_OLD_NAME ConsoleWrite("File renamed from: " & $sFileName & @CRLF) Case $FILE_ACTION_RENAMED_NEW_NAME ConsoleWrite("File renamed to: " & $sFileName & @CRLF) EndSwitch EndFunc ;==>FileNotifyUser Func CleanUp() _WinAPI_CancelIoEx($hDirectory, $tOverlapped) _WinAPI_CloseHandle($hDirectory) _WinAPI_FreeMemory($pDataBuffer) _WinAPI_CloseHandle($hEvent) DllCallbackFree($hCompletion) EndFunc ;==>CleanUp Func CompletionRoutine($nError, $nTransfered, $pOverlapped) Local $pBuffer = $pDataBuffer, $tNotify, $nLength While True $nLength = DllStructGetData(DllStructCreate("dword", $pBuffer + 8), 1) $tNotify = DllStructCreate("dword NextOffset;dword Action;dword NameLength;wchar FileName[" & $nLength / 2 & "]", $pBuffer) FileNotifyUser($tNotify.Action, $tNotify.FileName) If Not $tNotify.NextOffset Then ExitLoop $pBuffer += $tNotify.NextOffset WEnd _WinAPI_ReadDirectoryChangesW($hDirectory, $NOTIFY_FLAGS, $pDataBuffer, $BUFFER_LENGTH, 0, $tOverlapped, $pCompletion) EndFunc ;==>CompletionRoutine Func _WinAPI_ReadDirectoryChangesW($hDirectory, $iFilter, $pBuffer, $iLength, $bSubtree = 0, $tOverlapped = 0, $pCompletion = 0) Local $aRet = DllCall('kernel32.dll', 'bool', 'ReadDirectoryChangesW', 'handle', $hDirectory, 'struct*', $pBuffer, _ 'dword', $iLength - Mod($iLength, 4), 'bool', $bSubtree, 'dword', $iFilter, 'dword*', 0, 'struct*', $tOverlapped, 'PTR', $pCompletion) If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, 0) Return SetExtended(_WinAPI_GetLastError(), $aRet[0]) EndFunc ;==>_WinAPI_ReadDirectoryChangesW Func _WinAPI_CancelIoEx($hFile, $tOverlapped) Local $aRet = DllCall('kernel32.dll', 'bool', 'CancelIoEx', 'handle', $hFile, 'struct*', $tOverlapped) If @error Then Return SetError(@error, @extended, 0) Return $aRet[0] EndFunc ;==>_WinAPI_CancelIoEx Func _WinAPI_WaitForSingleObjectEx($hEvent, $nMilliseconds, $bAlertable = 0) Local $aRet = DllCall('kernel32.dll', 'dword', 'WaitForSingleObjectEx', 'handle', $hEvent, 'dword', $nMilliseconds, 'bool', $bAlertable) If @error Then Return SetError(@error, @extended, 0) Return $aRet[0] EndFunc ;==>_WinAPI_WaitForSingleObjectEx Func Terminate() Exit EndFunc ;==>Terminate Edited August 14 by Nine made example script argumentum, ioa747 and hudsonhock 1 2 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
argumentum Posted August 14 Share Posted August 14 (edited) Beautiful @Nine. Did change the buffer size from 4k to 64k as otherwise creating and removing 200 files at once would not catch them all. Any reason to the set a small buffer on this version ? My counter: ... ... Func FileNotifyUser($nFileAction, $sFileName) Local Static $iCount = 0 Switch $nFileAction Case $FILE_ACTION_ADDED $iCount += 1 ConsoleWrite($iCount & @TAB & "File added: " & $sFileName & @CRLF) Case $FILE_ACTION_REMOVED $iCount -= 1 ... ... Edited August 14 by argumentum more hudsonhock 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
Nine Posted August 14 Share Posted August 14 4 minutes ago, argumentum said: Any reason to the set a small buffer Nope, just a bad habit of saving space argumentum 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
hudsonhock Posted August 15 Author Share Posted August 15 Appreciate it very much @Nine Your fun save my day : ) I added some stuff to made detect size change and exit if no activity for more than 5 seconds. Global Const $NOTIFY_FLAGS = BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE) While True _WinAPI_WaitForSingleObjectEx($hEvent, 100, 1) ; Wait for 100ms ; Check if 5 seconds have passed since the last activity If TimerDiff($iLastActivityTime) > 5000 Then ConsoleWrite("No activity for 5 seconds. Exiting..." & @CRLF) ExitLoop EndIf WEnd Func FileNotifyUser($nFileAction, $sFileName) $iLastActivityTime = TimerInit() ; Reset the activity timer ;remain unchanged Thank you all for the help! Link to comment Share on other sites More sharing options...
Nine Posted August 15 Share Posted August 15 (edited) @hudsonhock Few suggestions. 1- By specifying a timeout on _WinAPI_WaitForSingleObjectEx, you disable the ability to use the HotKeySet (or any other means to stop the script) 2- You could use result of _WinAPI_WaitForSingleObjectEx to determine if an event occurred or not, would make the script more readable. Local $iRes, $hTimer = TimerInit() While TimerDiff($hTimer) < 5 * 60 * 1000 ; 5 mins $iRes = _WinAPI_WaitForSingleObjectEx($hEvent, 0, 1) If $iRes = $WAIT_IO_COMPLETION Then $hTimer = TimerInit() ; $WAIT_IO_COMPLETION = 0xC0 Sleep(100) WEnd ConsoleWrite("No activity for 5 minutes. Exiting..." & @CRLF) Edited August 15 by Nine hudsonhock 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now