4

I've been trying to get ReadDirectoryChangesW to monitor a subtree for file changes, but I have found that I am getting inconsistent results. The following is a self contained test case which illustrates the problem. When I run this it sometimes produces:

A : Created
C : Updated
A : Deleted

Another time it might produce:

A : Created
B : Updated
C : Updated
A : Deleted

I create a huge buffer, and the number of files being changed is very small (3 files).

The code:

import os, sys, time, threading
import win32file, win32event, win32con, pywintypes

class ChangeFiles ( threading.Thread ) :
    def run( self ) :
        files = [ 'A', 'B', 'C' ]
        time.sleep( 1 )
        for path in files : f = open( path, 'w' ); f.write( 'mooo' ); f.close()
        time.sleep( 0.5 )
        for path in files : os.remove( path )

ChangeFiles().start()

FILE_LIST_DIRECTORY = 0x0001

handle = win32file.CreateFile (
  '.',
  FILE_LIST_DIRECTORY,
  win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
  None,
  win32con.OPEN_EXISTING,
  win32con.FILE_FLAG_BACKUP_SEMANTICS | win32file.FILE_FLAG_OVERLAPPED,
  None
)

buffer = win32file.AllocateReadBuffer( 1024 * 64 )
overlapped = pywintypes.OVERLAPPED()
overlapped.hEvent = win32event.CreateEvent( None, 0, 0, None )

readFlags = win32con.FILE_NOTIFY_CHANGE_FILE_NAME  | \
            win32con.FILE_NOTIFY_CHANGE_DIR_NAME   | \
            win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | \
            win32con.FILE_NOTIFY_CHANGE_SIZE       | \
            win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | \
            win32con.FILE_NOTIFY_CHANGE_SECURITY

ACTIONS = { 1 : "Created", 2 : "Deleted", 3 : "Updated" }

while 1 :
    win32file.ReadDirectoryChangesW( handle, buffer, False, readFlags, overlapped )

    rc = win32event.WaitForSingleObject( overlapped.hEvent, 200 )

    if rc == win32event.WAIT_OBJECT_0 :
        nbytes = win32file.GetOverlappedResult( handle, overlapped, True )
        if nbytes > 0 :
            for action, file in win32file.FILE_NOTIFY_INFORMATION( buffer, nbytes ) :
                print '%s : %s' % ( file, ACTIONS.get ( action, "Unknown" ) )
        else :
            print 'no bytes'
            break
    elif rc < 0 :
        print 'Error: %d' % win32api.GetLastError()
        break
Eric Vasilik
  • 362
  • 2
  • 14
  • I'm beginning to guess this is a situation where, if a tree falls in the forest and there is no one around to hear it, does it still make a sound? In this case, if there is no outstanding call to ReadDirectoryChangesW, and a change happens to the file system, will you later learn about the change? Perhaps not. I have been using a python library called watchdog which seems to be working for me, but I'd still like to know why my code sample above misses events. – Eric Vasilik Jan 06 '13 at 18:03
  • 1
    According to the [documentation](http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx), _Directory changes that occur between calls to this function are added to the buffer and then returned with the next call._ So events should be buffered until your next call. I've never used python, but I haven't had any problems with this API in native code. – Luke Jan 07 '13 at 15:45

1 Answers1

0

The issue here is that the timeout you're passing to WaitForSingleObject is very low, so the function tends to time out before you get notified about the events. Your code does not check that condition, hence you see no output (try testing for rc > 0, too).

You can fix this by passing the value of INFINITE (i.e. 0xFFFFFFFF) to WaitForObject.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207