winapireaddirectorychangesw

ReadDirectoryChangesW Asynchronous Completion routine called after I close the handle


I am using ReadDirectoryChangesW to monitor when a file has changed within a directory. I am using the asynchronous version of it with a completion routine function, (as per the docs).

Everything works fine until I wish to stop monitoring the folder.

To stop the monitoring I call the Close function.

The problem is that I still get one last notification, but by then I have destroyed my LPOVERLAPPED value.

How can I stop ReadDirectoryChangesW and prevent my MyCompletionRoutine function from being called.

// get the handle    
_handle = CreateFileW( ... )

void Read()
{
  ...
  ReadDirectoryChangesW( _handle, ..., &MyCompletionRoutine );
  ...
}

void Close()
{
  ::CancelIo(_handle );           
  ::CloseHandle(_handle );
}

void __stdcall MyCompletionRoutine (
    const unsigned long dwErrorCode,
    const unsigned long dwNumberOfBytesTransfered,
    _OVERLAPPED* lpOverlapped )
{
  // ... do stuff and start a read again
  Read();
}

In the code above I might have called Read but I want to stop before MyCompletionRoutine is called.

Not sure if that helps, but the error message I get is 317


Solution

  • You are closing your HANDLE and freeing your OVERLAPPED too soon.

    CancelIo() (and its cross-thread brother, CancelIoEx()) simply mark active I/O operations as cancelled and then exit, but you still need to actually wait for those operations to fully complete before you can then free their OVERLAPPED.

    If an operation notices the cancellation before completing its work, it will stop its work and report a completion with an error code of ERROR_OPERATION_ABORTED, otherwise it will finish its work normally and report a completion with the appropriate error code.

    After calling CancelIo/Ex(), you need to continue waiting on and handling completed operations, until there are no more operations left to wait on.

    In other words, MyCompletionRoutine() can indeed be called after CancelIo() is called, and it needs to check for ERROR_OPERATION_ABORTED before calling Read() again. And if there is a pending read in progress when CancelIo() is called, you need to wait for that read to complete.