AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

detect file and directory changes
Goto page 1, 2  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Thu Jan 05, 2006 4:45 am    Post subject: detect file and directory changes Reply with quote

The included code encapsulates and demonstrates an OS facility to detect changes to files and directories. The required functions are present with all versions of Windows.

The monitored events are:
    * FILE_NAME
    * DIR_NAME
    * ATTRIBUTES
    * SIZE
    * LAST_WRITE
    * SECURITY

note: further information can be found at MSDN

Code:

#Persistent

OnExit, HandleExit

; note: event(s) can be present in any combination
FindFirstChangeNotification( "testdir", true, "FILE_NAME|DIR_NAME|ATTRIBUTES|SIZE|LAST_WRITE|SECURITY", handles, array_handles )

SetTimer, timer_DetectChange, 1000
return

HandleExit:
   FindCloseChangeNotification( handles )
ExitApp

timer_DetectChange:
   loop,
   {
      index := WaitForMultipleObjects( array_handles )
      if ( index >= 1 and index <= 6 )
      {
         ; note: index number correlates with order of event present during call
         ;   to FindFirstChangeNotification
         if ( index = 1 )
            MsgBox, change has been detected: FILE_NAME
         else if ( index = 2 )
            MsgBox, change has been detected: DIR_NAME
         else if ( index = 3 )
            MsgBox, change has been detected: ATTRIBUTES
         else if ( index = 4 )
            MsgBox, change has been detected: SIZE
         else if ( index = 5 )
            MsgBox, change has been detected: LAST_WRITE
         else if ( index = 6 )
            MsgBox, change has been detected: SECURITY
            
         FindNextChangeNotification( handles, index )
      }
      else
         break
   }
return

EncodeInteger( p_value, p_size, p_address, p_offset )
{
   loop, %p_size%
      DllCall( "RtlFillMemory"
         , "uint", p_address+p_offset+A_Index-1
         , "uint", 1
         , "uchar", ( p_value >> ( 8*( A_Index-1 ) ) ) & 0xFF )
}

; return value: true = success, false = failure
FindFirstChangeNotification( p_path, p_recurse, p_events, byref r_handles, byref r_array_handles )
{
   r_handles=
   VarSetCapacity( r_array_handles, 25, 0 )

   count = 0
   loop, parse, p_events, |
   {
      count++
   
      if ( A_LoopField = "FILE_NAME" )
         event = 1
      else if ( A_LoopField = "DIR_NAME" )
         event = 2
      else if ( A_LoopField = "ATTRIBUTES" )
         event = 4
      else if ( A_LoopField = "SIZE" )
         event = 8
      else if ( A_LoopField = "LAST_WRITE" )
         event = 0x10
      else if ( A_LoopField = "SECURITY" )
         event = 0x100
   
      h_notification := DllCall( "FindFirstChangeNotification", "str", p_path, "int", p_recurse, "uint", event )
      if ( ErrorLevel or h_notification = -1 )
      {
         MsgBox, [FindFirstChangeNotification] failed: EL = %ErrorLevel% ~ h_notification = %h_notification%
         return, false
      }
      
      r_handles = %r_handles%|%h_notification%
      
      EncodeInteger( h_notification, 4, &r_array_handles, 1+( A_Index-1 )*4 )
   }
   
   r_handles = %r_handles%|
   
   EncodeInteger( count, 1, &r_array_handles, 0 )
   
   return, true
}

; return value: true = success, false = failure
FindNextChangeNotification( p_handles, p_index )
{
   StringGetPos, ix, p_handles, |, L%p_index%
   StringMid, handle, p_handles, ix+2, InStr( p_handles, "|", true, ix+2 )-( ix+2 )
   
   return, DllCall( "FindNextChangeNotification", "uint", handle )
}

; return value: true = success, false = failure
FindCloseChangeNotification( p_handles )
{
   StringMid, p_handles, p_handles, 2, StrLen( p_handles )-2

   result := true
   loop, parse, p_handles, |
      result := result && DllCall( "FindCloseChangeNotification", "uint", A_LoopField )
      
   return, result
}

; return value: [1,6], otherwise failure
WaitForMultipleObjects( byref r_array_handles )
{
   result := DllCall( "WaitForMultipleObjects", "uint", *( &r_array_handles ), "uint", &r_array_handles+1, "int", false, "uint", 0 )
   
   if ( result >= 0 and result < 6 )
      return, result+1
   else
      return, result
}


Last edited by shimanov on Thu Jan 05, 2006 10:19 pm; edited 1 time in total
Back to top
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10480

PostPosted: Thu Jan 05, 2006 1:31 pm    Post subject: Reply with quote

Something like this should probably be included in the standard library when that feature finally appears.

Thanks for your R&D and friendly implementation.
Back to top
View user's profile Send private message Send e-mail
not-logged-in-daonlyfreez
Guest





PostPosted: Thu Jan 05, 2006 1:44 pm    Post subject: Reply with quote

Very nice! Smile

Would it be possible to return not only a change, but also which files/folders changed and what change was detected on them?
Back to top
kapege.de



Joined: 07 Feb 2005
Posts: 188
Location: Munich, Germany

PostPosted: Thu Jan 05, 2006 5:35 pm    Post subject: Reply with quote

Fine work!

Buuuut: The polling is the main problem. To ask the drives every second for changes isn't the state of art. Sorry pal. Windows has an inbuilt function which broadcasts any changes of the file system. Sorrily I don't know much about the file system and DLL-calls, but it should be possible to intercept those messages and react.

(Sorry for the unwanted early sending)
_________________
Peter

Wisenheiming for beginners: KaPeGe (German only, sorry)
Back to top
View user's profile Send private message Visit poster's website
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Thu Jan 05, 2006 6:02 pm    Post subject: Reply with quote

Chris wrote:
Thanks for your R&D and friendly implementation.


Sure. Happy to contribute.

daonlyfreez wrote:
Would it be possible to return not only a change, but also which files/folders changed and what change was detected on them?


There is another Windows function, ReadDirectoryChangesW, but it requires a callback function and is only present with Windows NT 4.0 and later. Otherwise, identification of changes will require a custom implementation. You may find useful information in another thread, Storing MD5 Hash in a Variable using DllCall().

kapege.de wrote:
ask the drives every second for changes


I cannot say with absolute certainty, but I don't believe this is the method employed. It is more likely there is code in the path for file system access, which monitors for those changes configured by calling FindFirstChangeNotification. When an event matching that requested occurs, a flag is set and the invoking process will be notified. I used the WaitForMultipleObjects function precisely because it permits polling and can be used within AHk. Review RegisterWaitForSingleObject for a fully asynchronous method to receive notification. There are other methods, but they are outside the scope of this forum.

To verify my assertion, use File Monitor from Sysinternals, which captures file system access at a lower (driver) level.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4078
Location: Pittsburgh

PostPosted: Thu Jan 05, 2006 6:34 pm    Post subject: Reply with quote

I get an error (h_notification = -1) in FindFirstChangeNotification. What does it mean?
Back to top
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Thu Jan 05, 2006 7:34 pm    Post subject: Reply with quote

Laszlo wrote:
I get an error (h_notification = -1) in FindFirstChangeNotification. What does it mean?


Have you verified that h_notification = -1 or ErrorLevel is non-zero? It would also help if you post the code used to call FindFirstChangeNotification.

MSDN wrote:
If the function fails, the return value is INVALID_HANDLE_VALUE = -1. To get extended error information, call GetLastError.


Unfortunately, GetLastError does not return relevant information when called from AHk using DllCall. You can try the same calls from a native program (e.g., C program) to perform further diagnostics.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4078
Location: Pittsburgh

PostPosted: Tue Jan 10, 2006 12:07 am    Post subject: Reply with quote

Laszlo wrote:
I get an error (h_notification = -1) in FindFirstChangeNotification. What does it mean?
It meant, the directory "testdir" did not exist. My fault! When I changed it to "c:\t", which does exist, everything works!
Back to top
View user's profile Send private message
AHKnow



Joined: 03 Jul 2004
Posts: 118

PostPosted: Mon Mar 13, 2006 3:37 am    Post subject: Reply with quote

Just seen this. Very nice.
Back to top
View user's profile Send private message
AHKnow*
Guest





PostPosted: Tue Mar 14, 2006 8:42 pm    Post subject: Reply with quote

not-logged-in-daonlyfreez wrote:
Very nice! Smile

Would it be possible to return not only a change, but also which files/folders changed and what change was detected on them?


I agree. Specific information is needed to make this script more useful. You need to know which files and what change.
Back to top
majkinetor



Joined: 24 May 2006
Posts: 3652
Location: Belgrade

PostPosted: Wed Sep 13, 2006 10:43 am    Post subject: Reply with quote

Nice.
_________________
Back to top
View user's profile Send private message MSN Messenger
Mystiq



Joined: 08 Jan 2007
Posts: 19

PostPosted: Fri May 18, 2007 1:13 pm    Post subject: Reply with quote

Is it possible to monitor multiple directories?

Ive checked the code but im not really sure, might be out of my expertise i think. Smile

update:
Nevermind, i think i got it. Just awesome. Smile
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3652
Location: Belgrade

PostPosted: Fri May 18, 2007 1:54 pm    Post subject: Reply with quote

 y
_________________
Back to top
View user's profile Send private message MSN Messenger
Mystiq



Joined: 08 Jan 2007
Posts: 19

PostPosted: Fri May 18, 2007 7:50 pm    Post subject: Reply with quote

After lot of confusion ive managed to "wrap" these with new functions so that im easily able to monitor as many directories as i need but im wondering if its "recommended" to set up a lot of them?

Mainly im thinking of not using the recursive option so i can "limit" my search for the file(s) that have been added/changed. Basically, have directory and its subfolders all separate.

Any thoughts on this?
Back to top
View user's profile Send private message
Mystiq



Joined: 08 Jan 2007
Posts: 19

PostPosted: Sat May 19, 2007 1:02 pm    Post subject: Reply with quote

Desided not to use mutliple instances for each directory but theres one question.

Does this capture all the changes? What ive tested so far, these functions only capture up to two changes max. I mean that if i create two new files i get two file_name events. If i make more than two, it still gives only two file_name events. Is this normal?

edit:
Bit of searching i think found the answer. It seems that theres is a maximum of 2 per event. According to this website:

http://netez.com/2xExplorer/shellFAQ/bas_xplore.html
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group