Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

detect file and directory changes


  • Please log in to reply
36 replies to this topic
shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
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
* SECURITYnote: further information can be found at MSDN

#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
}


Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Something like this should probably be included in the standard library when that feature finally appears.

Thanks for your R&D and friendly implementation.

not-logged-in-daonlyfreez
  • Guests
  • Last active:
  • Joined: --
Very nice! :)

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

kapege.de
  • Members
  • 192 posts
  • Last active: Jan 16 2012 12:34 PM
  • Joined: 07 Feb 2005
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)

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Thanks for your R&D and friendly implementation.


Sure. Happy to contribute.

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().

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.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I get an error (h_notification = -1) in FindFirstChangeNotification. What does it mean?

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

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.

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.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

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!

AHKnow
  • Members
  • 121 posts
  • Last active: May 17 2009 09:11 PM
  • Joined: 03 Jul 2004
Just seen this. Very nice.

AHKnow*
  • Guests
  • Last active:
  • Joined: --

Very nice! :)

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.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Nice.
Posted Image

Mystiq
  • Members
  • 83 posts
  • Last active: Nov 06 2011 07:07 PM
  • Joined: 08 Jan 2007
Is it possible to monitor multiple directories?

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

update:
Nevermind, i think i got it. Just awesome. :)

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
 y
Posted Image

Mystiq
  • Members
  • 83 posts
  • Last active: Nov 06 2011 07:07 PM
  • Joined: 08 Jan 2007
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?

Mystiq
  • Members
  • 83 posts
  • Last active: Nov 06 2011 07:07 PM
  • Joined: 08 Jan 2007
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:

<!-- m -->http://netez.com/2xE...bas_xplore.html<!-- m -->