AutoHotkey Community

It is currently May 26th, 2012, 6:37 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 57 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
 Post subject: WatchDirectory()
PostPosted: June 13th, 2009, 12:01 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
This functionality already exist in Crazy Scripting : FolderSpy v0.96 [ Synchronous ]
Many thanks to SKAN and Lexikos ;)

WatchDirectory() is also available for AHK_L/H with Unicode support.

I have been working on this function quite a long time, trying to understand everything :?
Finally I have put together an all in one function to handle that procedure. :D

- You can add a new folder to watch for.
- Report changes to any function
- Stop watching

Hope you like it and find it useful. :wink:

Please report any bugs or suggestions.

WatchDirectory(WatchFolder,WatchSubDirs) wrote:
Parameters
WatchFolder - Specify a valid path to watch for changes in.
- can be directory or drive (e.g. c:\ or c:\Temp)
- can be network path e.g. \\192.168.2.101\Shared)
- can include last backslash. e.g. C:\Temp\ (will be reported same form)

WatchSubDirs - Specify 1 to search in subfolders

StopWatching - THIS SHOULD BE DONE BEFORE EXITING SCRIPT AT LEAST (OnExit)
Call WatchDirectory() without parameters to stop watching all directories

ReportChanges
Call WatchDirectory("ReportingFunctionName") to process registered changes.
Syntax of ReportingFunctionName(Action,Folder,File)

Windows 2000++ required


Easy example
Code:
#Persistent
      OnExit,Exit
      WatchDirectory("C:\Windows",1)
      SetTimer,WatchFolder,100
      Return
      WatchFolder:
         WatchDirectory("RegisterChanges")
      Return
      RegisterChanges(action,folder,file){
         static
         #1:="New File", #2:="Deleted", #3:="Modified", #4:="Renamed From", #5:="Renamed To"
         ToolTip % #%Action% "`n" folder . (SubStr(folder,0)="" ? "" : "") . file
      }   
      Exit:
         WatchDirectory()
      ExitApp


Function and a script that I used for testing:

Code:
#Persistent
OnExit, GuiClose

WatchFolders=C:\Temp*|%A_Temp%*|%A_Desktop%|%A_DesktopCommon%|%A_MyDocuments%*|%A_ScriptDir%|%A_WinDir%*

Gui,Add,ListView,r10 w600 vWatchingDirectoriesList gShowDir,WatchingDirectories|WatchingSubdirs
Loop,Parse,WatchFolders,|
   WatchDirectory(SubStr(A_LoopField,0)="*" ? (SubStr(A_LoopField,1,StrLen(A_LoopField)-1)) : A_LoopField
               , SubStr(A_LoopField,0)="*" ? 1 : "")
               ,LV_Add("",SubStr(A_LoopField,0)="*" ? (SubStr(A_LoopField,1,StrLen(A_LoopField)-1)) : A_LoopField
               , SubStr(A_LoopField,0)="*" ? 1 : 0)
LV_ModifyCol(1,"AutoHdr")
Gui,Add,ListView,r30 w600 vChangesList gShowFile,Action|Dir|FileName  -  Double click to open in explorer
Gui,Add,Button,gAdd Default,Watch new directory
Gui,Add,Button,gDelete x+1,Stop watching all directories
Gui,Add,Button,gClear x+1,Clear List
Gui,Add,StatusBar,,Changes Registered
Gui, Show

SetTimer, WatchFolder, -100
Return

Clear:
Gui,ListView, ChangesList
LV_Delete()
Return

Delete:
WatchDirectory()
Gui,ListView, WatchingDirectoriesList
LV_Delete()
Gui,ListView, ChangesList
TotalChanges:=0
SB_SetText("Changes Registered")
Return

ShowDir:
If A_GuiEvent!=DoubleClick
   Return
Gui,ListView,%A_GuiControl%
LV_GetText(folder,A_EventInfo,1)
Run,% "explorer.exe /e`, /n`, /select`," . folder
Return
ShowFile:
If A_GuiEvent!=DoubleClick
   Return
Gui,ListView,%A_GuiControl%
LV_GetText(folder,A_EventInfo,2)
LV_GetText(file,A_EventInfo,3)
Run,% "explorer.exe /e`, /n`, /select`," . (folder . "" . file)
Return

Add:
Gui,+OwnDialogs
FileSelectFolder,dir,,3,Select directory to watch for
SetTimer,SetMsgBoxButtons,-50
MsgBox, 262146,Add directory,Would you like to watch for changes in:`n%dir%
SplashTextOn,600,0,Adding %dir% ...
Gui,ListView, WatchingDirectoriesList
IfMsgBox Retry
   WatchDirectory(dir),LV_Add("",dir,1)
IfMsgBox Ignore
   WatchDirectory(dir,0),LV_Add("",dir,0)
SplashTextOff
LV_ModifyCol(1,"AutoHdr")
Gui,ListView, ChangesList
Return

SetMsgBoxButtons:
   ControlSetText,Button2,&Incl. subdirs, ahk_class #32770
   ControlSetText,Button3,&Excl. subdirs, ahk_class #32770
Return

WatchFolder:
   WatchDirectory("ReportDirectoryChanges")
   SetTimer, WatchFolder, -10
Return

ReportDirectoryChanges(Action,folder,file){
   static
   #1:="New File", #2:="Deleted", #3:="Modified", #4:="Renamed From", #5:="Renamed To"
   ;ToolTip % #%Action% "`n" folder . (SubStr(folder,0)="" ? "" : "") . file
   path:=folder . (SubStr(folder,0)="" ? "" : "") . file
   Gui,ListView, ChangesList
   SplitPath,path,filename,dir
   LV_Add("",#%Action%,dir,filename)
   LV_ModifyCol()
   LV_ModifyCol(3,"AutoHdr")
   TotalChanges++
   SB_SetText("Changes Registered " . TotalChanges)
}

GuiClose:
WatchDirectory()
ExitApp




;Function WatchDirectory()
;
;Parameters
;      WatchFolder         - Specify a valid path to watch for changes in.
;                     - can be directory or drive (e.g. c:\ or c:\Temp)
;                     - can be network path e.g. \\192.168.2.101\Shared)
;                     - can include last backslash. e.g. C:\Temp\ (will be reported same form)
;
;      WatchSubDirs      - Specify whether to search in subfolders
;
;StopWatching   -   THIS SHOULD BE DONE BEFORE EXITING SCRIPT AT LEAST (OnExit)
;      Call WatchDirectory() without parameters to stop watching all directories
;
;ReportChanges
;      Call WatchDirectory("ReportingFunctionName") to process registered changes.
;      Syntax of ReportingFunctionName(Action,Folder,File)
;
;      Example
/*
      #Persistent
      OnExit,Exit
      WatchDirectory("C:\Windows",1)
      SetTimer,WatchFolder,100
      Return
      WatchFolder:
         WatchDirectory("RegisterChanges")
      Return
      RegisterChanges(action,folder,file){
         static
         #1:="New File", #2:="Deleted", #3:="Modified", #4:="Renamed From", #5:="Renamed To"
         ToolTip % #%Action% "`n" folder . (SubStr(folder,0)="" ? "" : "") . file
      }   
      Exit:
         WatchDirectory()
      ExitApp
*/

WatchDirectory(WatchFolder="", WatchSubDirs=true)
{
   static
   local hDir, hEvent, r, Action, FileNameLen, pFileName, Restart, CurrentFolder, PointerFNI, _SizeOf_FNI_=65536
   nReadLen := 0
   If !(WatchFolder){
      Gosub, StopWatchingDirectories
   } else if IsFunc(WatchFolder) {
      r := DllCall("MsgWaitForMultipleObjectsEx", UInt, DirIdx, UInt, &DirEvents, UInt, -1, UInt, 0x4FF, UInt, 0x6) ;Timeout=-1
      if !(r >= 0 && r < DirIdx)
         Return
      r += 1
      CurrentFolder := Dir%r%Path
      PointerFNI := &Dir%r%FNI
      DllCall( "GetOverlappedResult", UInt, hDir, UInt, &Dir%r%Overlapped, UIntP, nReadLen, Int, true )
      Loop {
         pNext      := NumGet( PointerFNI + 0  )
         Action      := NumGet( PointerFNI + 4  )
         FileNameLen := NumGet( PointerFNI + 8  )
         pFileName :=       ( PointerFNI + 12 )
         If (Action < 0x6){
            VarSetCapacity( FileNameANSI, FileNameLen )
            DllCall( "WideCharToMultiByte",UInt,0,UInt,0,UInt,pFileName,UInt,FileNameLen,Str,FileNameANSI,UInt,FileNameLen,UInt,0,UInt,0)
            %WatchFolder%(Action,CurrentFolder,SubStr( FileNameANSI, 1, FileNameLen/2 ))
         }
         If (!pNext or pNext = 4129024)
            Break
         Else
            PointerFNI := (PointerFNI + pNext)
      }
      DllCall( "ResetEvent", UInt,NumGet( Dir%r%Overlapped, 16 ) )
      Gosub, ReadDirectoryChanges
      return r
   } else {
      Loop % (DirIdx) {
         If InStr(WatchFolder, Dir%A_Index%Path){
            If (Dir%A_Index%Subdirs)
               Return
         } else if InStr(Dir%A_Index%Path, WatchFolder) {
            If (WatchSubDirs){
               DllCall( "CloseHandle", UInt,Dir%A_Index% )
               DllCall( "CloseHandle", UInt,NumGet(Dir%A_Index%Overlapped, 16) )
               Restart := DirIdx, DirIdx := A_Index
            }
         }
      }
      If !Restart
         DirIdx += 1
      r:=DirIdx
      hDir := DllCall( "CreateFile"
                , Str  , WatchFolder
                , UInt , ( FILE_LIST_DIRECTORY := 0x1 )
                , UInt , ( FILE_SHARE_READ     := 0x1 )
                     | ( FILE_SHARE_WRITE    := 0x2 )
                     | ( FILE_SHARE_DELETE   := 0x4 )
                , UInt , 0
                , UInt , ( OPEN_EXISTING := 0x3 )
                , UInt , ( FILE_FLAG_BACKUP_SEMANTICS := 0x2000000  )
                     | ( FILE_FLAG_OVERLAPPED       := 0x40000000 )
                , UInt , 0 )
      Dir%r%         := hDir
      Dir%r%Path     := WatchFolder
      Dir%r%Subdirs  := WatchSubDirs
      VarSetCapacity( Dir%r%FNI, _SizeOf_FNI_ )
      VarSetCapacity( Dir%r%Overlapped, 20, 0 )
      DllCall( "CloseHandle", UInt,hEvent )
      hEvent := DllCall( "CreateEvent", UInt,0, Int,true, Int,false, UInt,0 )
      NumPut( hEvent, Dir%r%Overlapped, 16 )
      if ( VarSetCapacity(DirEvents) < DirIdx*4 and VarSetCapacity(DirEvents, DirIdx*4 + 60))
         Loop %DirIdx%
         {
            If (SubStr(Dir%A_Index%Path,1,1)!="-"){
               action++
               NumPut( NumGet( Dir%action%Overlapped, 16 ), DirEvents, action*4-4 )
            }
         }
      NumPut( hEvent, DirEvents, DirIdx*4-4)
      Gosub, ReadDirectoryChanges
      If Restart
         DirIdx = %Restart%
   }
   Return
   StopWatchingDirectories:
      Loop % (DirIdx) {
         DllCall( "CloseHandle", UInt,Dir%A_Index% )
         DllCall( "CloseHandle", UInt,NumGet(Dir%A_Index%Overlapped, 16) )
         Dir%A_Index%=
         Dir%A_Index%Path=
         Dir%A_Index%Subdirs=
         Dir%A_Index%FNI=
         DllCall( "CloseHandle", UInt, NumGet(Dir%A_Index%Overlapped,16) )
         VarSetCapacity(Dir%A_Index%Overlapped,0)
      }
      DirIdx=
      VarSetCapacity(DirEvents,0)
   Return
   ReadDirectoryChanges:
      DllCall( "ReadDirectoryChangesW"
         , UInt , Dir%r%
         , UInt , &Dir%r%FNI
         , UInt , _SizeOf_FNI_
         , UInt , Dir%r%SubDirs
         , UInt , ( 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_CREATION    := 0x40  )
               | ( FILE_NOTIFY_CHANGE_SECURITY    := 0x100 )
         , UInt , 0
         , UInt , &Dir%r%Overlapped
         , UInt , 0  )
   Return
}

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Last edited by HotKeyIt on November 11th, 2010, 1:12 am, edited 3 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 13th, 2009, 11:36 am 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Thanks for this!

I was wondeirng if this will 'monitor' all dir's recursed from root(s)?

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 12:22 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
TLM wrote:
Thanks for this!

I was wondeirng if this will 'monitor' all dir's recursed from root(s)?


WatchDirectory("C:",1) is definitely possible, I am not sure if WatchDirectory("\\192.168.2.102",1) will work, I will test tomorrow.

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 1:40 am 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Just a heads up, there are errors in both versions starting with 'static'. I think it should be 'static:'???
After that the 1st WatchDirectory func is not recognized.

I'd really like it if the easy version worked (at least I can somewhat understand it) ;)..

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 2:01 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
TLM wrote:
Just a heads up, there are errors in both versions starting with 'static'. I think it should be 'static:'???
After that the 1st WatchDirectory func is not recognized.

I'd really like it if the easy version worked (at least I can somewhat understand it) ;)..


static mode in WatchDirectory is used to keep variables inside the function so you have no access to these outside it.
In ReportDirectoryChanges static mode is used to keep those variables loaded
Code:
#1:="New File", #2:="Deleted", #3:="Modified", #4:="Renamed From", #5:="Renamed To"

As far as I understand otherwise they would need to be assigned each time calling that function.
Quote:
After that the 1st WatchDirectory func is not recognized.

What exactly do you mean?
When I change WatchFolders to
Code:
WatchFolders=%A_WinDir%*|C:\Temp*

I get all changes reported?
Is this not working for you?

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 2:34 am 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Heres what I get..
Image
When static is changed to static:
Image

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 2:53 am 
Online

Joined: March 27th, 2008, 2:14 pm
Posts: 700
Wow! Thanks! I've wanted this so many times!

Quick question: Does it only report acculumulated changes up until WatchDirectory() is called with a function name?

TLM: is your autohotkey version current? 1.48***

_________________
Scripts - License


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 6:40 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
infogulch wrote:
Wow! Thanks! I've wanted this so many times!

Quick question: Does it only report acculumulated changes up until WatchDirectory() is called with a function name?


Thank you me too :D
Yes your are right, as far as I understand but please bear in mind:
MSDN wrote:
When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded and the lpBytesReturned parameter contains zero.

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 9:22 pm 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Wish I could get this to work :cry:

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 9:24 pm 
Offline

Joined: April 18th, 2008, 7:57 am
Posts: 1390
Location: The Interwebs
TLM wrote:
Wish I could get this to work :cry:

You need to upgrade to the latest AHK version, which supports assume-static functions.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 14th, 2009, 10:10 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
TLM wrote:
Wish I could get this to work :cry:

->http://www.autohotkey.com/download/

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 15th, 2009, 11:28 am 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Not to be a pita but I installed the latest version and I get this
Image
I really need this function to work.

Any assitance please

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 15th, 2009, 12:53 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
TLM wrote:
Not to be a pita but I installed the latest version and I get this
Image
I really need this function to work.

Any assitance please




This is now because you did not put the function in your Lib folder
e.g. C:\Program Files\AutoHotkey\Lib\WatchDirectory.ahk

But you can also use #include, e.g.
Code:
#include C:\Program Files\AutoHotkey\AutoScriptWriter\WatchDirectory.ahk


When you copy the whole script + function at the top into a file (not the easy example), doesn't it work for you?

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 15th, 2009, 4:10 pm 
Online

Joined: March 27th, 2008, 2:14 pm
Posts: 700
HotKeyIt:

Did you test if it works with UNC paths? The ReadDirectoryChangesW function doesn't even mention it, but maybe a mapped network drive would work.

Thx again!

_________________
Scripts - License


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: June 15th, 2009, 7:22 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
infogulch wrote:
HotKeyIt:

Did you test if it works with UNC paths? The ReadDirectoryChangesW function doesn't even mention it, but maybe a mapped network drive would work.

Thx again!

Yes I tested on my home PC from Laptop watching \\192.168.2.103\data and it worked very well, as well as on a mapped drive :)
I was surprised that this works :o

Edit:
Though it does not work for an online mapped drive :(

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 57 posts ]  Go to page 1, 2, 3, 4  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Apollo, Jaaaaaaaaay, Ragnar, Retro Gamer and 10 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group