Zomp Posted April 16, 2008 Share Posted April 16, 2008 (edited) Please note: the idea of this script is not mine, but of AutoHotKey user SKAN:http://www.autohotkey.com/forum/viewtopic....light=folderspyThe script does very well what it promises: it monitors any file change in a folder (and its subfolders).I asked to SKAN (and obtained) the permission to convert his script in AutoIt language.It was not very difficult to traslate the script, but I encountered a problem and I will ask you some help to make it working.My GUI is more more stark than the original one. In particular, I do not insert changed file rows on the list view, but I call only consolewrite function to signal an event, and I use a "start" button instead of a "start/stop" button.It seems to me that the other parts of the script are a faithful copies of the original ones.Nevertheless, the script does not work. Indeed, pay attention at the sleep function at line 137:1) If I launch the script after uncommenting it, I obtain the "Error parsing funcion call" message2) If I launch the script leaving it commented, the script proceeds but without signaling any event (see consolewrite function at line 134 which will to be substituted by a call to Decode_FILE_NOTIFY_INFORMATION)I will be very happy if someone is able to indicate me what has gone wrong.Thanks for your attention.expandcollapse popup#include <GUIConstants.au3> Global $hDir,$PointerFNI, $SizeOfFNI, $WatchSubdirs, $nReadlen Global const $FILE_LIST_DIRECTORY = 0x1, _ $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _ $FILE_FLAG_OVERLAPPED = 0x40000000 ; $FILE_SHARE_READ = 0x1, _ ; $FILE_SHARE_WRITE = 0x2, _ ; $OPEN_EXISTING = 0x3, _ ; $FILE_SHARE_DELETE = 0x4, _ ; are already declared in GUIConstants.au3 Global const _ $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _ $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _ $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _ $FILE_NOTIFY_CHANGE_SIZE = 0x8, _ $FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10, _ $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _ $FILE_NOTIFY_CHANGE_CREATION = 0x40, _ $FILE_NOTIFY_CHANGE_SECURITY = 0x100 $FNI=DllStructCreate("char FNI[10000]") if @error Then MsgBox(0,"","Error in DllStructCreate " & @error); exit endif $T1="00" for $i=1 to 15 $T1&=$T1 Next DllStructSetData($FNI,1,$T1) if @error Then MsgBox(0,"","Error in DllStructSetData " & @error); exit endif $WatchFolder = "c:\temp\temp1\" $WatchSubDirs = True $SizeOfFNI=0x10000 $PointerFNI=DllStructGetPtr($FNI) $handle = DLLCallbackRegister ("ReadDirectoryChanges", "Uint", "") $aHandle=DllCallbackGetPtr($handle) Opt("GUIOnEventMode", 1) $mainwindow = GUICreate("FolderSpy Clone") $startbutton = GUICtrlCreateButton("Start", 10, 10, 60) GUICtrlCreateListView("Time|Event|File/Folder Name|Size-KB|TimeStamp [Mod]|Attrib",10,50,350,300,-1) GUICtrlSetOnEvent($startbutton, "StartButton") GUISetOnEvent($GUI_EVENT_CLOSE, "CLOSEClicked") GUISetState(@SW_SHOW) while 1 sleep(1000) Wend exit func ReadDirectoryChanges() consolewrite("RDC" & @CRLF) ; http://msdn2.microsoft.com/en-us/library/aa365465.aspx $RDC =DllCall( _ "kernel32.dll","Int","ReadDirectoryChangesW", _ "UInt" , $hDir, _ "UInt" , $PointerFNI, _ "UInt" , $SizeOfFNI, _ "UInt" , $WatchSubDirs, _ "UInt" , $FILE_NOTIFY_CHANGE_FILE_NAME _ +$FILE_NOTIFY_CHANGE_DIR_NAME _ +$FILE_NOTIFY_CHANGE_ATTRIBUTES _ +$FILE_NOTIFY_CHANGE_SIZE _ +$FILE_NOTIFY_CHANGE_LAST_WRITE _ +$FILE_NOTIFY_CHANGE_LAST_ACCESS _ +$FILE_NOTIFY_CHANGE_CREATION _ +$FILE_NOTIFY_CHANGE_SECURITY, _ "UInt*", $nReadLen, _ "UInt" , 0, _ "UInt" , 0 _ ) if not @error=0 then msgbox(0,"ReadDirectoryChangesW",@error) Return $RDC EndFunc func StartButton() ; CreateFile: http://msdn2.microsoft.com/en-us/library/aa914735.aspx $hDir=DllCall("kernel32.dll","Int","CreateFile", _ "Str" , $WatchFolder , _ "UInt" , $FILE_LIST_DIRECTORY , _ "UInt" , $FILE_SHARE_READ+$FILE_SHARE_WRITE+$FILE_SHARE_DELETE , _ "UInt" , 0 , _ "UInt" , $OPEN_EXISTING , _ "UInt", $FILE_FLAG_BACKUP_SEMANTICS + $FILE_FLAG_OVERLAPPED , _ "UInt" , 0 _ ) if not @error=0 then msgbox(0,"CreateFile",@error) while true $nReadLen = 0 $hThreadId = 0 ; CreateThread : http://msdn2.microsoft.com/en-us/library/ms682453.aspx $hThread = DllCall( "kernel32.dll", "Int","CreateThread", _ "UInt",0, _ "UInt",0, _ "UInt",$aHandle , _ "UInt",0, _ "UInt",0, _ "UInt*",$hThreadId _ ) if not @error=0 then msgbox(0,"CreateThread",@error) while true If $nReadLen then ConsoleWrite($nreadlen) ExitLoop endif ;Sleep 100 wend ; TerminateThread : http://msdn2.microsoft.com/en-us/library/ms686717.aspx ; CloseHandle : http://msdn2.microsoft.com/en-us/library/ms724211.aspx DllCall( "kernel32.dll","Int","TerminateThread", "UInt",$hThread, "UInt",0 ) if not @error=0 then msgbox(0,"TerminateThread",@error) DllCall( "kernel32.dll","Int","CloseHandle", "UInt",$hThread ) if not @error=0 then msgbox(0,"CloseHandle",@error) wend Return EndFunc func CLOSEClicked() exit EndFunc Edited September 1, 2008 by Zomp Link to comment Share on other sites More sharing options...
zorphnog Posted April 16, 2008 Share Posted April 16, 2008 You forgot your parentheses, line 137 should be: Sleep(100) Link to comment Share on other sites More sharing options...
weaponx Posted April 16, 2008 Share Posted April 16, 2008 You forgot your parentheses, line 137 should be: Sleep(100)Its commented out. I ran Tidy on the source and its fine. Link to comment Share on other sites More sharing options...
zorphnog Posted April 16, 2008 Share Posted April 16, 2008 Perhaps I should have clarified what I was replying to.Indeed, pay attention at the sleep function at line 137:1) If I launch the script after uncommenting it, I obtain the "Error parsing funcion call" message2) If I launch the script leaving it commented, the script proceeds but without signaling any event (see consolewrite function at line 134 which will to be substituted by a call to Decode_FILE_NOTIFY_INFORMATION) Link to comment Share on other sites More sharing options...
Zomp Posted April 16, 2008 Author Share Posted April 16, 2008 You forgot your parentheses, line 137 should be: Sleep(100)Yes, you are right. Problem 1) is solved. Thanks.I am not able yet to understand problem 2), that is why file modifications/aaditions are not notified. Link to comment Share on other sites More sharing options...
weaponx Posted April 16, 2008 Share Posted April 16, 2008 Perhaps you would be better off using WMI, ptrex has an example here:http://www.autoitscript.com/forum/index.ph...2511&hl=wmi Link to comment Share on other sites More sharing options...
zorphnog Posted April 16, 2008 Share Posted April 16, 2008 I don't think you are going to be able to implement your original post because AutoIt is not thread-safe. http://www.autoitscript.com/forum/index.ph...st&p=190906 Link to comment Share on other sites More sharing options...
SkinnyWhiteGuy Posted April 16, 2008 Share Posted April 16, 2008 I read SKAN's post, and saw that he did his this way due to the blocking caused by the Synchronous method used. He said that an Asynchronous method could be used, and then wouldn't need the threads (if I understood right). Look into that. Link to comment Share on other sites More sharing options...
Zomp Posted April 16, 2008 Author Share Posted April 16, 2008 Perhaps you would be better off using WMI, ptrex has an example here:http://www.autoitscript.com/forum/index.ph...2511&hl=wmiIt's practically impossible to monitor a directory tree using WMI. Read also VeeDub post:http://www.autoitscript.com/forum/index.ph...l=wmi&st=45 Link to comment Share on other sites More sharing options...
Zomp Posted April 16, 2008 Author Share Posted April 16, 2008 I don't think you are going to be able to implement your original post because AutoIt is not thread-safe. http://www.autoitscript.com/forum/index.ph...st&p=190906I suspected so, even if in the script at least one thread is created and a call to function ReadDirectoryChange is made. But evidently this is not sufficient to prove that more than one thread can work. Link to comment Share on other sites More sharing options...
Zomp Posted April 16, 2008 Author Share Posted April 16, 2008 I read SKAN's post, and saw that he did his this way due to the blocking caused by the Synchronous method used. He said that an Asynchronous method could be used, and then wouldn't need the threads (if I understood right). Look into that.I will try! Link to comment Share on other sites More sharing options...
arcker Posted April 21, 2008 Share Posted April 21, 2008 your code is complete ? because i don't see anything in the listview and more than that, the cpu is about 100% when i run it did i miss something ? -- Arck System _ Soon -- Ideas make everything "La critique est facile, l'art est difficile" Projects :[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013Â Get it Here [/list] Link to comment Share on other sites More sharing options...
weaponx Posted April 21, 2008 Share Posted April 21, 2008 your code is complete ?because i don't see anything in the listviewand more than that, the cpu is about 100% when i run itdid i miss something ?Who are you replying to? Link to comment Share on other sites More sharing options...
Zomp Posted April 21, 2008 Author Share Posted April 21, 2008 (edited) your code is complete ? because i don't see anything in the listview and more than that, the cpu is about 100% when i run it did i miss something ? Do not worry about listview content, I have not completed the code for it. Just clic the Start button and check the console. Anyhow, the script should work, but it does not. The line ConsoleWrite($nreadlen)is never invoked. I'm trying to make some variations using always a call to ReadDirectoryChangeW but without success. Edited April 21, 2008 by Zomp Link to comment Share on other sites More sharing options...
SkinnyWhiteGuy Posted April 21, 2008 Share Posted April 21, 2008 I did some testing with threads, and, as the Devs who know these things have repeated several times, AutoIt doesn't do well with threads. In a more specific way, once you create a new thread and it starts running, it doesn't give up it's control back to the main thread till it's done, which might be why your not seeing anything (haven't tested your code that much). Link to comment Share on other sites More sharing options...
Zomp Posted April 21, 2008 Author Share Posted April 21, 2008 (edited) I did some testing with threads, and, as the Devs who know these things have repeated several times, AutoIt doesn't do well with threads. In a more specific way, once you create a new thread and it starts running, it doesn't give up it's control back to the main thread till it's done, which might be why your not seeing anything (haven't tested your code that much).Yes, also other people did advice me. In the thread on AHK forum I mention, there is a version of FolderSpy which does not need to create new threads. So I tried to translate it in AutoIt language, but it does not work, too. Maybe I made some mistakes (I am only a newbie), or maybe there are some other problem. Maybe there is someone interested in doing this job better than me. Edited April 21, 2008 by Zomp Link to comment Share on other sites More sharing options...
zorphnog Posted April 21, 2008 Share Posted April 21, 2008 Post it. Link to comment Share on other sites More sharing options...
Zomp Posted April 21, 2008 Author Share Posted April 21, 2008 Post it. Sorry, I realized to have lost the code I have tempted to write so many script that I have made a great confusion. Here is another attempt I have made using _DllCallBack http://www.autoitscript.com/forum/index.php?showtopic=50768 to avoid creation of threads. expandcollapse popup#include "DllCallBack.au3" #include <GUIConstants.au3> Global $hDir,$PointerFNI, $SizeOfFNI, $WatchSubdirs, $nReadlen Global const $FILE_LIST_DIRECTORY = 0x1, _ $FILE_FLAG_BACKUP_SEMANTICS = 0x2000000, _ $FILE_FLAG_OVERLAPPED = 0x40000000 ; $FILE_SHARE_READ = 0x1, _ ; $FILE_SHARE_WRITE = 0x2, _ ; $OPEN_EXISTING = 0x3, _ ; $FILE_SHARE_DELETE = 0x4, _ ; are already declared in GUIConstants.au3 Global const _ $FILE_NOTIFY_CHANGE_FILE_NAME = 0x1, _ $FILE_NOTIFY_CHANGE_DIR_NAME = 0x2, _ $FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x4, _ $FILE_NOTIFY_CHANGE_SIZE = 0x8, _ $FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10, _ $FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x20, _ $FILE_NOTIFY_CHANGE_CREATION = 0x40, _ $FILE_NOTIFY_CHANGE_SECURITY = 0x100 $FNI=DllStructCreate("char FNI[10000]") if @error Then MsgBox(0,"","Error in DllStructCreate " & @error); exit endif $T1="00" for $i=1 to 15 $T1&=$T1 Next DllStructSetData($FNI,1,$T1) if @error Then MsgBox(0,"","Error in DllStructSetData " & @error); exit endif $diroverlapped=DllStructCreate("char diroverlapped[20]") if @error Then MsgBox(0,"","Error in DllStructCreate " & @error); exit endif $T1="000000000000000000000000000000000" DllStructSetData($diroverlapped,1,$T1) if @error Then MsgBox(0,"","Error in DllStructSetData " & @error); exit endif $direvents=DllStructCreate("char direvents[20]") if @error Then MsgBox(0,"","Error in DllStructCreate " & @error); exit endif $T1="000000000000000000000000000000000" DllStructSetData($direvents,1,$T1) if @error Then MsgBox(0,"","Error in DllStructSetData " & @error); exit endif $WatchFolder = "c:\temp\temp1\" $WatchSubDirs = True $SizeOfFNI=0x10000 $PointerFNI=DllStructGetPtr($FNI) $SizeOfoverlapped=0x20 $PointerOverlapped=DllStructGetPtr($diroverlapped) $SizeOfDirevents=0x20 $Pointerdirevents=DllStructGetPtr($direvents) msgbox(0,"",$Pointerdirevents) $handle = _DLLCallback("Timer1", "HWND hWnd;UINT uMsg;UINT idEvent;DWORD dwTime") ;$aHandle=DllCallbackGetPtr($handle) $hDir=DllCall("kernel32.dll","Int","CreateFile", _ "Str" , $WatchFolder , _ "UInt" , $FILE_LIST_DIRECTORY , _ "UInt" , $FILE_SHARE_READ+$FILE_SHARE_WRITE+$FILE_SHARE_DELETE , _ "UInt" , 0 , _ "UInt" , $OPEN_EXISTING , _ "UInt", $FILE_FLAG_BACKUP_SEMANTICS + $FILE_FLAG_OVERLAPPED , _ "UInt" , 0 _ ) if not @error=0 then msgbox(0,"CreateFile",@error) $RDC =DllCall( _ "kernel32.dll","UInt","ReadDirectoryChangesW", _ "UInt" , $hDir, _ "UInt" , $PointerFNI, _ "UInt" , $SizeOfFNI, _ "UInt" , $WatchSubDirs, _ "UInt" , $FILE_NOTIFY_CHANGE_FILE_NAME _ +$FILE_NOTIFY_CHANGE_DIR_NAME _ +$FILE_NOTIFY_CHANGE_ATTRIBUTES _ +$FILE_NOTIFY_CHANGE_SIZE _ +$FILE_NOTIFY_CHANGE_LAST_WRITE _ +$FILE_NOTIFY_CHANGE_LAST_ACCESS _ +$FILE_NOTIFY_CHANGE_CREATION _ +$FILE_NOTIFY_CHANGE_SECURITY, _ "UInt", 0, _ "UInt" , 0, _ "UInt" , 0 _ ) if not @error=0 then msgbox(0,"ReadDirectoryChangesW",@error) $hTimer1 = _SetTimer($handle, 1000) while 1 sleep(1000) Wend exit Func _SetTimer($pTimerFunc, $nTime, $nID = 0, $hWnd = 0) Local $aTmp = DllCall("user32.dll", "uint", "SetTimer", "hwnd", $hWnd, "uint", $nID, "uint", $nTime, "ptr", $pTimerFunc) If @error Then Return SetError(1, 0, 0) If $aTmp[0] = 0 Then SetError(2) Return $aTmp[0] EndFunc ;==>_SetTimer Func Timer1($hWnd, $uMsg, $idEvent, $dwTime) ; http://msdn2.microsoft.com/en-us/library/aa365465.aspx $r=DllCall("kernel32.dll","UInt", "MsgWaitForMultipleObjectsEx", "UInt", 1, "UInt", $Pointerdirevents _ , "UInt", -1, "UInt", 0x4FF, "UInt", 0x6) if $r=0 then; WAIT_OBJECT_* $nReadLen = 0 DllCall("kernel32.dll","UInt", "GetOverlappedResult", "UInt", $hDir _ , "UInt", $Pointeroverlapped, "UInt*", $nReadLen, "Int", true ) consolewrite ("*") endif $RDC =DllCall( _ "kernel32.dll","UInt","ReadDirectoryChangesW", _ "UInt" , $hDir, _ "UInt" , $PointerFNI, _ "UInt" , $SizeOfFNI, _ "UInt" , $WatchSubDirs, _ "UInt" , $FILE_NOTIFY_CHANGE_FILE_NAME _ +$FILE_NOTIFY_CHANGE_DIR_NAME _ +$FILE_NOTIFY_CHANGE_ATTRIBUTES _ +$FILE_NOTIFY_CHANGE_SIZE _ +$FILE_NOTIFY_CHANGE_LAST_WRITE _ +$FILE_NOTIFY_CHANGE_LAST_ACCESS _ +$FILE_NOTIFY_CHANGE_CREATION _ +$FILE_NOTIFY_CHANGE_SECURITY, _ "UInt", 0, _ "UInt" , 0, _ "UInt" , 0 _ ) if not @error=0 then msgbox(0,"ReadDirectoryChangesW",@error) if $nreadlen then ConsoleWrite($nreadlen) endif EndFunc ;==>Timer1 Link to comment Share on other sites More sharing options...
arcker Posted April 21, 2008 Share Posted April 21, 2008 (edited) i'm working on it, and here what i've constated : - why thread ? thread not needed here - your filenotifystructure are wrong, as the createfile call - you need a buffer to get all the changes (i'm blocked here) - you don't get what type of event occured (creation, deletion, and so on) if somebody can tell me how to get a buffer in autoit, would be cute here is the C++ code (remark the line FILE_NOTIFY_INFORMATION Buffer[1024] <= How to do that un AutoIt ??) expandcollapse popup{ USES_CONVERSION; HANDLE hDir = CreateFile( CString("c:\\Program Files"), // pointer to the file name FILE_LIST_DIRECTORY, // access (read/write) mode FILE_SHARE_READ|FILE_SHARE_DELETE, // share mode NULL, // security descriptor OPEN_EXISTING, // how to create FILE_FLAG_BACKUP_SEMANTICS, // file attributes NULL // file with attributes to copy ); FILE_NOTIFY_INFORMATION Buffer[1024]; DWORD BytesReturned; while( ReadDirectoryChangesW( hDir, // handle to directory &Buffer, // read results buffer sizeof(Buffer), // length of buffer TRUE, // monitoring option FILE_NOTIFY_CHANGE_SECURITY| FILE_NOTIFY_CHANGE_CREATION| FILE_NOTIFY_CHANGE_LAST_ACCESS| FILE_NOTIFY_CHANGE_LAST_WRITE| FILE_NOTIFY_CHANGE_SIZE| FILE_NOTIFY_CHANGE_ATTRIBUTES| FILE_NOTIFY_CHANGE_DIR_NAME| FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions &BytesReturned, // bytes returned NULL, // overlapped buffer NULL// completion routine )) { CTime tm = CTime::GetCurrentTime(); CString helper_txt; switch(Buffer[0].Action) { case FILE_ACTION_ADDED: helper_txt = "The file was added to the directory"; break; case FILE_ACTION_REMOVED: helper_txt = "The file was removed from the directory"; break; case FILE_ACTION_MODIFIED: helper_txt = "The file was modified. This can be a change in the time stamp or attributes."; break; case FILE_ACTION_RENAMED_OLD_NAME: helper_txt = "The file was renamed and this is the old name."; break; case FILE_ACTION_RENAMED_NEW_NAME: helper_txt = "The file was renamed and this is the new name."; break; } int i=0; do { m_Sec.Lock(); int item = pList1->InsertItem(pList1->GetItemCount(), CString(Buffer[i].FileName).Left(Buffer[i].FileNameLength / 2) + " - " + helper_txt ); pList1->SetItemText(item, 1, tm.Format("%Y/%m/%d/ - %H:%M:%S")); i++; m_Sec.Unlock(); } while (!Buffer[i].NextEntryOffset); } } Edited April 21, 2008 by arcker -- Arck System _ Soon -- Ideas make everything "La critique est facile, l'art est difficile" Projects :[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013Â Get it Here [/list] Link to comment Share on other sites More sharing options...
zorphnog Posted April 21, 2008 Share Posted April 21, 2008 Global $tag_FILE_NOTIFY_INFORMATION = "dword NextEntryOffset;dword Action;dword FileNameLength;str FileName;" $tFNI = DllStructCreate($tag_FILE_NOTIFY_INFORMATION) $ptrFNI = DllStructGetPtr($tFNI) ;==> pointer to FNI struct $sizeFNI = DllStructGetSize($tFNI) ;==> size of FNI struct (buffer) 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