AutoHotkey Community

It is currently May 27th, 2012, 11:59 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 91 posts ]  Go to page 1, 2, 3, 4, 5 ... 7  Next
Author Message
PostPosted: November 11th, 2010, 1:09 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 ] and WatchDirectory()
Again many thanks to SKAN and Lexikos ;)

Note :!: You will need latest AutoHotkey_L or AutoHotkey_H to use these functions.

Also latest _Struct() class is required.

It's now better, faster, more accurate and has got great filtering options. Enjoy ;)

How To Use wrote:
[b]All you need is a function in your script that accepts 2 parameters

- When a file is changed, both parameters will contain same filename/path
- When a file is deleted, it is passed in first parameter and second parameter will be empty
- When a new file is created, first parameter is empty and second contains filename/path
- When a file is renamed, first parameter contains old filename/path and second new filename/path

WatchDirectory(dir[\*|...],func,watchfor) accepts up to 3 parameters
- dir, can be a folder/directory (last backslash is optional)
- - include * in the end of your directory to watch in sub folders
- - after that you can include file name filters separated by pipe '|', here you have following options:
- - - use * and ? wildcard
- - - include Back Slash in front = match beginning of filename
- - - include Back Slash at the end = match end of filename
- - - include Back Slash anywhere in the middle to search in complete file path
- - - E.g. following will search for files that start with a and end with .txt or .ini
Code:
WatchDirectory("C:\temp\*|.txt\|.ini\|\a","ReportChanges")

- func, a function to be launched when changes in corresponding folder occur
- - when you omit func, last used function will be used
- Your function must accept 2 parameters
- - When new file is created:
- - - first parameter will be empty
- - - second parameter will contain the filepath
- - When file is deleted
- - - second parameter will be empty
- - - first parameter will contain the filepath
- - When file is modified both parameters contain same filepath
- - When file is renamed
- - - first parameter will contain old filename
- - - second parameter will contain new filename
- watchfor can be used to report only specific changes
- - therefore include one or several of following values i
- - 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
- - these values must be separated by |, by default all changes are reported.

Code:
WatchDirectory("C:\Temp","ReportFunction",0x1|0x2|0x8|0x40)


To stop watching Directory, at least when script exits WatchDirectory needs to be called with empty parameter
Code:
WatchDirectory("")

To stop watching a specific Directory, include "" for second parameter. E.g.
Code:
WatchDirectory("C:\Temp","")


Example and WatchDirectory() Function
Code:
#Persistent
SetBatchLines,-1
SetWinDelay,-1
OnExit, GuiClose

WatchFolders=C:\Temp*|%A_Temp%*|%A_Desktop%|%A_DesktopCommon%|%A_MyDocuments%*|%A_ScriptDir%|%A_WinDir%*
Gui,+Resize
Gui,Add,ListView,r10 w800 vWatchingDirectoriesList HWNDhList1 gShow,WatchingDirectories|WatchingSubdirs
Loop,Parse,WatchFolders,|
   WatchDirectory(A_LoopField,"ReportChanges")
   ,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 w800 vChangesList HWNDhList2 gShow,Time|FileChangedFrom - Double click to show in Explorer|FileChangedTo - Double click to show 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

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

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

Add:
Gui,+OwnDialogs
dir=
FileSelectFolder,dir,,3,Select directory to watch for
If !dir
   Return
SetTimer,SetMsgBoxButtons,-10
MsgBox, 262146,Add directory,Would you like to watch for changes in:`n%dir%

if SubStr(dir,0)!="\"
  dir.="\"

Gui,ListView, WatchingDirectoriesList
IfMsgBox Retry
   WatchDirectory(dir "*"),LV_Add("",dir,1)
IfMsgBox Ignore
   WatchDirectory(dir),LV_Add("",dir,0)
LV_ModifyCol(1,"AutoHdr")
Gui,ListView, ChangesList
Return

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

ReportChanges(from,to){
   global TotalChanges
   Gui,ListView, ChangesList
   LV_Insert(1,"",A_Hour ":" A_Min ":" A_Sec ":" A_MSec,from,to)
   LV_ModifyCol()
   LV_ModifyCol(1,"AutoHdr"),LV_ModifyCol(2,"AutoHdr")
   TotalChanges++
   SB_SetText("Changes Registered " . TotalChanges)
}

GuiClose:
WatchDirectory("") ;Stop Watching Directory = delete all directories
ExitApp

#include <_Struct>
WatchDirectory(p*){
   global _Struct
   ;Structures
   static FILE_NOTIFY_INFORMATION:="DWORD NextEntryOffset,DWORD Action,DWORD FileNameLength,WCHAR FileName[1]"
   static OVERLAPPED:="ULONG_PTR Internal,ULONG_PTR InternalHigh,{struct{DWORD offset,DWORD offsetHigh},PVOID Pointer},HANDLE hEvent"
   ;Variables
   static running,sizeof_FNI=65536,temp1:=VarSetCapacity(nReadLen,8),WatchDirectory:=RegisterCallback("WatchDirectory","F",0,0)
   static timer,ReportToFunction,LP,temp2:=VarSetCapacity(LP,(260)*(A_PtrSize/2),0)
   static @:=Object(),reconnect:=Object(),#:=Object(),DirEvents,StringToRegEx="\\\|.\.|+\+|[\[|{\{|(\(|)\)|^\^|$\$|?\.?|*.*"
   ;ReadDirectoryChanges related
   static 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
   static FILE_ACTION_ADDED=1,FILE_ACTION_REMOVED=2,FILE_ACTION_MODIFIED=3
         ,FILE_ACTION_RENAMED_OLD_NAME=4,FILE_ACTION_RENAMED_NEW_NAME=5
   static OPEN_EXISTING=3,FILE_FLAG_BACKUP_SEMANTICS=0x2000000,FILE_FLAG_OVERLAPPED=0x40000000
         ,FILE_SHARE_DELETE=4,FILE_SHARE_WRITE=2,FILE_SHARE_READ=1,FILE_LIST_DIRECTORY=1
   If p.MaxIndex(){
      If (p.MaxIndex()=1 && p.1=""){
         for i,folder in #
            DllCall("CloseHandle","Uint",@[folder].hD),DllCall("CloseHandle","Uint",@[folder].O.hEvent)
            ,@.Remove(folder)
         #:=Object()
         DirEvents:=new _Struct("HANDLE[1000]")
         DllCall("KillTimer","Uint",0,"Uint",timer)
         timer=
         Return 0
      } else {
         if p.2
            ReportToFunction:=p.2
         If !IsFunc(ReportToFunction)
            Return -1 ;DllCall("MessageBox","Uint",0,"Str","Function " ReportToFunction " does not exist","Str","Error Missing Function","UInt",0)
         RegExMatch(p.1,"^([^/\*\?<>\|""]+)(\*)?(\|.+)?$",dir)
         if (SubStr(dir1,0)="")
            StringTrimRight,dir1,dir1,1
         StringTrimLeft,dir3,dir3,1
         If (p.MaxIndex()=2 && p.2=""){
            for i,folder in #
               If (dir1=SubStr(folder,1,StrLen(folder)-1))
                  Return 0 ,DirEvents[i]:=DirEvents[#.MaxIndex()],DirEvents[#.MaxIndex()]:=0
                           @.Remove(folder),#[i]:=#[#.MaxIndex()],#.Remove(i)
            Return 0
         }
      }
      if !InStr(FileExist(dir1),"D")
         Return -1 ;DllCall("MessageBox","Uint",0,"Str","Folder " dir1 " does not exist","Str","Error Missing File","UInt",0)
      for i,folder in #
      {
         If (dir1=SubStr(folder,1,StrLen(folder)-1) || (InStr(dir1,folder) && @[folder].sD))
               Return 0
         else if (InStr(SubStr(folder,1,StrLen(folder)-1),dir1 "") && dir2){ ;replace watch
            DllCall("CloseHandle","Uint",@[folder].hD),DllCall("CloseHandle","Uint",@[folder].O.hEvent),reset:=i
         }
      }
      LP:=SubStr(LP,1,DllCall("GetLongPathName","Str",dir1,"Uint",&LP,"Uint",VarSetCapacity(LP))) ""
      If !(reset && @[reset]:=LP)
         #.Insert(LP)
      @[LP,"dir"]:=LP
      @[LP].hD:=DllCall("CreateFile","Str",StrLen(LP)=3?SubStr(LP,1,2):LP,"UInt",0x1,"UInt",0x1|0x2|0x4
                  ,"UInt",0,"UInt",0x3,"UInt",0x2000000|0x40000000,"UInt",0)
      @[LP].sD:=(dir2=""?0:1)

      Loop,Parse,StringToRegEx,|
         StringReplace,dir3,dir3,% SubStr(A_LoopField,1,1),% SubStr(A_LoopField,2),A
      StringReplace,dir3,dir3,%A_Space%,\s,A
      Loop,Parse,dir3,|
      {
         If A_Index=1
            dir3=
         pre:=(SubStr(A_LoopField,1,2)="\"?2:0)
         succ:=(SubStr(A_LoopField,-1)="\"?2:0)
         dir3.=(dir3?"|":"") (pre?"\\\K":"")
               . SubStr(A_LoopField,1+pre,StrLen(A_LoopField)-pre-succ)
               . ((!succ && !InStr(SubStr(A_LoopField,1+pre,StrLen(A_LoopField)-pre-succ),""))?"[^\\]*$":"") (succ?"$":"")
      }
      @[LP].FLT:="i)" dir3
      @[LP].FUNC:=ReportToFunction
      @[LP].CNG:=(p.3?p.3:(0x1|0x2|0x4|0x8|0x10|0x40|0x100))
      If !reset {
         @[LP].SetCapacity("pFNI",sizeof_FNI)
         @[LP].FNI:=new _Struct(FILE_NOTIFY_INFORMATION,@[LP].GetAddress("pFNI"))
         @[LP].O:=new _Struct(OVERLAPPED)
      }
      @[LP].O.hEvent:=DllCall("CreateEvent","Uint",0,"Int",1,"Int",0,"UInt",0)
      If (!DirEvents)
         DirEvents:=new _Struct("HANDLE[1000]")
      DirEvents[reset?reset:#.MaxIndex()]:=@[LP].O.hEvent
      DllCall("ReadDirectoryChangesW","UInt",@[LP].hD,"UInt",@[LP].FNI[],"UInt",sizeof_FNI
               ,"Int",@[LP].sD,"UInt",@[LP].CNG,"UInt",0,"UInt",@[LP].O[],"UInt",0)
      Return timer:=DllCall("SetTimer","Uint",0,"UInt",timer,"Uint",50,"UInt",WatchDirectory)
   } else {
      Sleep, 0
      for LP in reconnect
      {
         If (FileExist(@[LP].dir) && reconnect.Remove(LP)){
            DllCall("CloseHandle","Uint",@[LP].hD)
            @[LP].hD:=DllCall("CreateFile","Str",StrLen(@[LP].dir)=3?SubStr(@[LP].dir,1,2):@[LP].dir,"UInt",0x1,"UInt",0x1|0x2|0x4
                  ,"UInt",0,"UInt",0x3,"UInt",0x2000000|0x40000000,"UInt",0)
            DllCall("ResetEvent","UInt",@[LP].O.hEvent)
            DllCall("ReadDirectoryChangesW","UInt",@[LP].hD,"UInt",@[LP].FNI[],"UInt",sizeof_FNI
               ,"Int",@[LP].sD,"UInt",@[LP].CNG,"UInt",0,"UInt",@[LP].O[],"UInt",0)
         }
      }
      if !( (r:=DllCall("MsgWaitForMultipleObjectsEx","UInt",#.MaxIndex()
               ,"UInt",DirEvents[],"UInt",0,"UInt",0x4FF,"UInt",6))>=0
               && r<#.MaxIndex() ){
         return
      }
      DllCall("KillTimer", UInt,0, UInt,timer)
      LP:=#[r+1],DllCall("GetOverlappedResult","UInt",@[LP].hD,"UInt",@[LP].O[],"UIntP",nReadLen,"Int",1)
      If (A_LastError=64){ ; ERROR_NETNAME_DELETED - The specified network name is no longer available.
         If !FileExist(@[LP].dir) ; If folder does not exist add to reconnect routine
            reconnect.Insert(LP,LP)
      } else
         Loop {
            FNI:=A_Index>1?(new _Struct(FILE_NOTIFY_INFORMATION,FNI[]+FNI.NextEntryOffset)):(new _Struct(FILE_NOTIFY_INFORMATION,@[LP].FNI[]))
            If (FNI.Action < 0x6){
               FileName:=@[LP].dir . StrGet(FNI.FileName[""],FNI.FileNameLength/2,"UTF-16")
               If (FNI.Action=FILE_ACTION_RENAMED_OLD_NAME)
                     FileFromOptional:=FileName
               If (@[LP].FLT="" || RegExMatch(FileName,@[LP].FLT) || FileFrom)
                  If (FNI.Action=FILE_ACTION_ADDED){
                     FileTo:=FileName
                  } else If (FNI.Action=FILE_ACTION_REMOVED){
                     FileFrom:=FileName
                  } else If (FNI.Action=FILE_ACTION_MODIFIED){
                     FileFrom:=FileTo:=FileName
                  } else If (FNI.Action=FILE_ACTION_RENAMED_OLD_NAME){
                     FileFrom:=FileName
                  } else If (FNI.Action=FILE_ACTION_RENAMED_NEW_NAME){
                     FileTo:=FileName
                  }
          If (FNI.Action != 4 && (FileTo . FileFrom) !="")
                  @[LP].Func(FileFrom=""?FileFromOptional:FileFrom,FileTo)
            }
         } Until (!FNI.NextEntryOffset || ((FNI[]+FNI.NextEntryOffset) > (@[LP].FNI[]+sizeof_FNI-12)))
      DllCall("ResetEvent","UInt",@[LP].O.hEvent)
      DllCall("ReadDirectoryChangesW","UInt",@[LP].hD,"UInt",@[LP].FNI[],"UInt",sizeof_FNI
               ,"Int",@[LP].sD,"UInt",@[LP].CNG,"UInt",0,"UInt",@[LP].O[],"UInt",0)
      timer:=DllCall("SetTimer","Uint",0,"UInt",timer,"Uint",50,"UInt",WatchDirectory)
      Return
   }
   Return
}

AutoHotkey V2 version:
Code:
SetWinDelay,-1
OnExit, GuiClose

WatchFolders:="C:\Temp*|%A_Temp%*|%A_Desktop%|%A_DesktopCommon%|%A_MyDocuments%*|%A_ScriptDir%|%A_WinDir%*"
Gui,+Resize
Gui,Add,ListView,r10 w800 vWatchingDirectoriesList HWNDhList1 gShow,WatchingDirectories|WatchingSubdirs
LoopParse,%WatchFolders%,|
   WatchDirectory(A_LoopField,"ReportChanges")
   ,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 w800 vChangesList HWNDhList2 gShow,Time|FileChangedFrom - Double click to show in Explorer|FileChangedTo - Double click to show 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

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

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

Add:
Gui,+OwnDialogs
dir:=""
FileSelectFolder,dir,,3,Select directory to watch for
If !dir
   Return
SetTimer,SetMsgBoxButtons,-10
MsgBox, 262146,Add directory,Would you like to watch for changes in:`n%dir%

Gui,ListView, WatchingDirectoriesList
If (A_MsgBoxResult="Retry")
   WatchDirectory(dir "*"),LV_Add("",dir,1)
If (A_MsgBoxResult="Ignore")
   WatchDirectory(dir),LV_Add("",dir,0)
LV_ModifyCol(1,"AutoHdr")
Gui,ListView, ChangesList
Return

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

ReportChanges(from,to){
   global TotalChanges
   Gui,ListView, ChangesList
   LV_Insert(1,"",A_Hour ":" A_Min ":" A_Sec ":" A_MSec,from,to)
   LV_ModifyCol()
   LV_ModifyCol(1,"AutoHdr"),LV_ModifyCol(2,"AutoHdr")
   TotalChanges++
   SB_SetText("Changes Registered " . TotalChanges)
}

GuiClose:
WatchDirectory("") ;Stop Watching Directory = delete all directories
ExitApp

#include <_Struct>
WatchDirectory(p*){
   global _Struct
   ;Structures
   static FILE_NOTIFY_INFORMATION:="DWORD NextEntryOffset,DWORD Action,DWORD FileNameLength,WCHAR FileName[1]"
   static OVERLAPPED:="ULONG_PTR Internal,ULONG_PTR InternalHigh,{struct{DWORD offset,DWORD offsetHigh},PVOID Pointer},HANDLE hEvent"
   ;Variables
   static running,sizeof_FNI:=65536,temp1:=VarSetCapacity(nReadLen,8),WatchDirectory:=RegisterCallback("WatchDirectory","F",0,0)
   static timer,ReportToFunction,LP,temp2:=VarSetCapacity(LP,(260)*(A_PtrSize/2),0)
   static __:=Object(),reconnect:=Object(),_:=Object(),DirEvents,StringToRegEx:="\\\|.\.|+\+|[\[|{\{|(\(|)\)|^\^|$\$|?\.?|*.*"
   ;ReadDirectoryChanges related
   static 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
   static FILE_ACTION_ADDED:=1,FILE_ACTION_REMOVED:=2,FILE_ACTION_MODIFIED:=3
         ,FILE_ACTION_RENAMED_OLD_NAME:=4,FILE_ACTION_RENAMED_NEW_NAME:=5
   static OPEN_EXISTING:=3,FILE_FLAG_BACKUP_SEMANTICS:=0x2000000,FILE_FLAG_OVERLAPPED:=0x40000000
         ,FILE_SHARE_DELETE:=4,FILE_SHARE_WRITE:=2,FILE_SHARE_READ:=1,FILE_LIST_DIRECTORY:=1
   If p.MaxIndex(){
      If (p.MaxIndex()=1 && p.1=""){
         for i,folder in _
            DllCall("CloseHandle","Uint",__[folder].hD),DllCall("CloseHandle","Uint",__[folder].O.hEvent)
            ,__.Remove(folder)
         _:=Object()
         DirEvents:=new _Struct("HANDLE[1000]")
         DllCall("KillTimer","Uint",0,"Uint",timer)
         timer:=""
         Return 0
      } else {
         if p.2
            ReportToFunction:=p.2
         If !IsFunc(ReportToFunction)
            Return -1 ;DllCall("MessageBox","Uint",0,"Str","Function " ReportToFunction " does not exist","Str","Error Missing Function","UInt",0)
         RegExMatch(p.1,"^([^/\*\?<>\|""]+)(\*)?(\|.+)?$",dir)
         loop % dir.count()
            dir%A_Index%:=dir[A_Index]
         if (SubStr(dir1,-1)="")
            dir1:=SubStr(dir1,1,-1)
         dir3:=SubStr(dir3,2)
         If (p.MaxIndex()=2 && p.2=""){
            for i,folder in _
               If (dir1=SubStr(folder,1,-1))
                  Return 0 ,DirEvents[i]:=DirEvents[_.MaxIndex()],DirEvents[_.MaxIndex()]:=0
                           __.Remove(folder),_[i]:=_[_.MaxIndex()],_.Remove(i)
            Return 0
         }
      }
      if !InStr(FileExist(dir1),"D")
         Return -1 ;DllCall("MessageBox","Uint",0,"Str","Folder " dir1 " does not exist","Str","Error Missing File","UInt",0)
      for i,folder in _
      {
         If (dir1=SubStr(folder,1,-1) || (InStr(dir1,folder) && __[folder].sD))
               Return 0
         else if (InStr(SubStr(folder,1,-1),dir1 "") && dir2){ ;replace watch
            DllCall("CloseHandle","PTR",__[folder].hD),DllCall("CloseHandle","PTR",__[folder].O.hEvent),reset:=i
         }
      }
      LP:=SubStr(LP,1,DllCall("GetLongPathName","Str",dir1,"STR",LP,"Uint",VarSetCapacity(LP))) ""
      If !(reset && __[reset]:=LP)
         _.Insert(LP)
      __[LP,"dir"]:=LP
      __[LP].hD:=DllCall("CreateFile","Str",StrLen(LP)=3?SubStr(LP,1,2):LP,"UInt",0x1,"UInt",0x1|0x2|0x4
                  ,"PTR",0,"UInt",0x3,"UInt",0x2000000|0x40000000,"PTR",0)
      __[LP].sD:=(dir2=""?0:1)

      LoopParse,%StringToRegEx%,|
         StrReplace,dir3,%dir3%,% SubStr(A_LoopField,1,1),% SubStr(A_LoopField,2)
      StrReplace,dir3,%dir3%,%A_Space%,\s
      LoopParse,%dir3%,|
      {
         If A_Index=1
            dir3:=""
         pre:=(SubStr(A_LoopField,1,2)="\"?2:0)
         succ:=(SubStr(A_LoopField,-2)="\"?2:0)
         dir3.=(dir3?"|":"") (pre?"\\\K":"")
               . SubStr(A_LoopField,1+pre,StrLen(A_LoopField)-pre-succ)
               . ((!succ && !InStr(SubStr(A_LoopField,1+pre,StrLen(A_LoopField)-pre-succ),""))?"[^\\]*$":"") (succ?"$":"")
      }
      __[LP].FLT:="i)" dir3
      __[LP].FUNC:=ReportToFunction
      __[LP].CNG:=(p.3?p.3:(0x1|0x2|0x4|0x8|0x10|0x40|0x100))
      If !reset {
         __[LP].SetCapacity("pFNI",sizeof_FNI)
         __[LP].FNI:=new _Struct(FILE_NOTIFY_INFORMATION,__[LP].GetAddress("pFNI"))
         __[LP].O:=new _Struct(OVERLAPPED)
      }
      __[LP].O.hEvent:=DllCall("CreateEvent","Uint",0,"Int",1,"Int",0,"UInt",0)
      If (!DirEvents)
         DirEvents:=new _Struct("HANDLE[1000]")
      DirEvents[reset?reset:_.MaxIndex()]:=__[LP].O.hEvent
      DllCall("ReadDirectoryChangesW","UInt",__[LP].hD,"UInt",__[LP].FNI[""],"UInt",sizeof_FNI
               ,"Int",__[LP].sD,"UInt",__[LP].CNG,"UInt",0,"UInt",__[LP].O[""],"UInt",0)
      Return timer:=DllCall("SetTimer","Uint",0,"UInt",timer,"Uint",50,"UInt",WatchDirectory)
   } else {
      Sleep, 0
      for LP in reconnect
      {
         If (FileExist(__[LP].dir) && reconnect.Remove(LP)){
            DllCall("CloseHandle","Uint",__[LP].hD)
            __[LP].hD:=DllCall("CreateFile","Str",StrLen(__[LP].dir)=3?SubStr(__[LP].dir,1,2):__[LP].dir,"UInt",0x1,"UInt",0x1|0x2|0x4
                  ,"UInt",0,"UInt",0x3,"UInt",0x2000000|0x40000000,"UInt",0)
            DllCall("ResetEvent","UInt",__[LP].O.hEvent)
            DllCall("ReadDirectoryChangesW","UInt",__[LP].hD,"UInt",__[LP].FNI[""],"UInt",sizeof_FNI
               ,"Int",__[LP].sD,"UInt",__[LP].CNG,"UInt",0,"UInt",__[LP].O[""],"UInt",0)
         }
      }
      if !( (r:=DllCall("MsgWaitForMultipleObjectsEx","UInt",_.MaxIndex()
               ,"UInt",DirEvents[""],"UInt",0,"UInt",0x4FF,"UInt",6))>=0
               && r<_.MaxIndex() ){
         return
      }
      DllCall("KillTimer", UInt,0, UInt,timer)
      LP:=_[r+1],DllCall("GetOverlappedResult","UInt",__[LP].hD,"UInt",__[LP].O[""],"UIntP",nReadLen,"Int",1)
      If (A_LastError=64){ ; ERROR_NETNAME_DELETED - The specified network name is no longer available.
         If !FileExist(__[LP].dir) ; If folder does not exist add to reconnect routine
            reconnect.Insert(LP,LP)
      } else
         Loop {
            FNI:=A_Index>1?(new _Struct(FILE_NOTIFY_INFORMATION,FNI[""]+FNI.NextEntryOffset)):(new _Struct(FILE_NOTIFY_INFORMATION,__[LP].FNI[""]))
            If (FNI.Action < 0x6){
               FileName:=__[LP].dir . StrGet(FNI.FileName[""],FNI.FileNameLength/2,"UTF-16")
               If (FNI.Action=FILE_ACTION_RENAMED_OLD_NAME)
                     FileFromOptional:=FileName
               If (__[LP].FLT="" || RegExMatch(FileName,__[LP].FLT) || FileFrom)
                  If (FNI.Action=FILE_ACTION_ADDED){
                     FileTo:=FileName
                  } else If (FNI.Action=FILE_ACTION_REMOVED){
                     FileFrom:=FileName
                  } else If (FNI.Action=FILE_ACTION_MODIFIED){
                     FileFrom:=FileTo:=FileName
                  } else If (FNI.Action=FILE_ACTION_RENAMED_OLD_NAME){
                     FileFrom:=FileName
                  } else If (FNI.Action=FILE_ACTION_RENAMED_NEW_NAME){
                     FileTo:=FileName
                  }
          If (FNI.Action != 4 && (FileTo . FileFrom) !="")
                  __[LP].Func(FileFrom=""?FileFromOptional:FileFrom,FileTo)
            }
         } Until (!FNI.NextEntryOffset || ((FNI[""]+FNI.NextEntryOffset) > (__[LP].FNI[""]+sizeof_FNI-12)))
      DllCall("ResetEvent","UInt",__[LP].O.hEvent)
      DllCall("ReadDirectoryChangesW","UInt",__[LP].hD,"UInt",__[LP].FNI[""],"UInt",sizeof_FNI
               ,"Int",__[LP].sD,"UInt",__[LP].CNG,"UInt",0,"UInt",__[LP].O[""],"UInt",0)
      timer:=DllCall("SetTimer","Uint",0,"UInt",timer,"Uint",50,"UInt",WatchDirectory)
      Return
   }
   Return
}

Changes:
18.02.2012
- AHKv2 version
05.09.2011
- Fixed OVERLAPED structure and _Struct
31.08.2011
- Updated to use new _Struct class
11.03.2011
- Fixed a bug for StrGet FileName (FNI.FileNameLength/2)
02.03.2011
- Fixed a bug for ANSI AHK_L
08.01.2011
- Fixed a bug in filtering option when * was used.
- Fixed to report a change when a file is renamed and new or old filename do not match the filter.
12.11.2010
- Small bug fixes and a routine to reconnect network shared drives
11.11.2010
- Changed sizeof_FNI back to 65536 because a higher buffer seems to be not valid for network drives

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


Last edited by HotKeyIt on May 17th, 2012, 10:26 pm, edited 17 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 6th, 2011, 8:55 pm 
Offline

Joined: December 13th, 2006, 7:10 am
Posts: 118
Very nice. Thanks for the good work ;)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 8th, 2011, 4:09 am 
Offline

Joined: June 4th, 2005, 1:30 am
Posts: 113
Location: Stuttgart, Germany
Very nice work! Thanks. I am using this script daily.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 8th, 2011, 1:27 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Thanks guys, I'm glad you like it ;)

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 8th, 2011, 11:17 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Fixed a bug in filtering option when * was used.
Fixed to report a change when a file is renamed and new or old filename do not match the filter.

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


Report this post
Top
 Profile  
Reply with quote  
PostPosted: January 17th, 2011, 3:16 pm 
Offline

Joined: April 23rd, 2010, 11:22 am
Posts: 64
If a file is modified (with notepad) and I initialised with (as example)

Code:
WatchDirectory("C:\temp\*|.txt\|.ini\|\a","ReportChanges")


i.e. for all events

then the ReportChanges function is called twice with exactly the same arguments.

Is there any way to either (1) limit it to 1 call
(2) detect the first call so I can do nothing.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 17th, 2011, 3:46 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Simply remember previous parameters :)
Code:
WatchDirectory("C:\temp\*|.txt\|.ini\|\a","ReportChanges")
ReportChanges(from,to){
   static last
   If (last=from to)
      Return
   MsgBox % from "`n" to
   last:=from to
}

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject: @hotkeyIt
PostPosted: January 17th, 2011, 4:16 pm 
Offline

Joined: April 23rd, 2010, 11:22 am
Posts: 64
Thanks, it works but it stops the second instance, not the first.

I want to create a copy of the file with the modification made, but the first trigger copies the file prior to the changes.

Also, if you change the same file twice in succession, it will only trigger the first save.

I used your code and modified it with A_TickCount

Code:
Report(from, to,action){
   global
   static last
   static ticks

   If (last=from to)
{
  tmp:=A_TickCount - ticks
  ticks:=A_TickCount
  if tmp<= 100
      Return
}
   MsgBox % from "`n" to
   last:=from to
   ticks:=A_TickCount
}


I used a value of 100 - which seems to work (in my testing I got a value of 62 or 63 in about 50 test cases - although all were local HDD)


A user that is very quick might be able to save twice and hence trigger this once (but unlikely to make changes - this will not be used on computer modified files). I will play around with that value for the network though - 100 might be too quick.

Thanks for your help!

Edit: Forgot to add that I modified your original function to send the action code (FNI.Action) to the report function.


Last edited by capitalH on January 17th, 2011, 4:19 pm, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 18th, 2011, 1:15 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Here is another attempt :)
Code:
Report(from, to){
   static _from:=Object(),_to:=Object(), i
   i:=_to.MaxIndex()
   If !(_from[i]=from && _to[i]=to){
      _from.Insert(from),_to.Insert(to)
      i++
      SetTimer,Report,-30
   }
   Return
   Report:
      MsgBox % _from[i] "`n" _to[i]
      _from.Remove(i),_to.Remove(i)
      If i:=_to.MaxIndex()
         SetTimer,Report,-30
   Return
}

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 5:29 am 
Offline

Joined: October 5th, 2007, 7:10 pm
Posts: 21
I'm not sure how to install this.

I need to make a script which will monitor a folder. Each time a new picture is saved to that folder, the script should pass the name of the image to a program for further processing. And as far as I know this is possible only with the WatchDirectory() function.

Since I have the old/classic Autohotkey installed first I installed
AutoHotkey_L
But how do I install Struct() and WatchDirectory() ?

I downloaded Struct.ahk and copied in AutoHotkey\lib Is that ok ?
So probably I need to do the same with WatchDirectory.ahk but I couldn't find a final WatchDirectory.ahk


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 8:00 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
It is included in above example, just copy above example, delete all but WatchDirectory(p*)... and save as WatchDirectory.ahk in lib folder ;)

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 8:28 am 
Offline

Joined: October 5th, 2007, 7:10 pm
Posts: 21
I just did that and then I tried you code sample and Scite editor give this error :

(221) : ==> Call to nonexistent function.
Specifically: Until(!FNI.NextEntryOffset || ((FNI[]+FNI.NextEntryOffset) > (@[LP].FNI[]+sizeof_FNI-12)))

I tried also with the whole code - including the WatchDirectory function - and I get the same error.

I also tried two Autohotkey : AutoHotkey_L Unicode x86 and ANSI x86

I also compiled manually and after running the exe I get this error :

Error at line 87.

The following variable name contains an illegal character:
"p*"


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 8:54 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Try again now. To compile you will need correct AutoHotkeySC.bin in Compile folder.

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 4:17 pm 
Offline

Joined: October 5th, 2007, 7:10 pm
Posts: 21
Thank you. Now it works.

BUt I tried this and I get multiple message box for just a single file saved to that folder

Code:
#Persistent

WatchDirectory("J:\img Convert\rename*","Report") 
Report(test)
{
msgbox % test
}

I also tried your last code but I still get two messages

Code:
#Persistent

WatchDirectory("J:\img Convert\rename*","Report")   
Report(from, to){
   static _from:=Object(),_to:=Object(), i
   i:=_to.MaxIndex()
   If !(_from[i]=from && _to[i]=to){
      _from.Insert(from),_to.Insert(to)
      i++
      SetTimer,Report,-30
   }
   Return
   Report:
      MsgBox % _from[i] "`n" _to[i]
      _from.Remove(i),_to.Remove(i)
      If i:=_to.MaxIndex()
         SetTimer,Report,-30
   Return
}

EDIT :
And another problem seems to be - only the first letter of the file saved appears in msgbox. Like this :
J:\img Convert\rename\W
instead of : J:\img Convert\rename\Winter.jpg

EDIT 2 : I switched to the Unicode x86 Autohotkey and now I get the full name. But I still get multiple msgbox


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 2nd, 2011, 9:00 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Thanks I have fixed ANSI version.
See above post with regards to multiple messages.

_________________
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  [ 91 posts ]  Go to page 1, 2, 3, 4, 5 ... 7  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: notsoobvious, rrhuffy and 16 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