How to use ReadDirectoryChangesW method with completion routine?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
ReadDirectoryChangesW can watch a directory for file-system activity such as creates, deletes, renames, and content changes. When you use it with a completion routine, the call becomes asynchronous, so your thread can wait efficiently instead of blocking on one directory read.
The part that usually trips people up is not the function call itself. It is the surrounding Win32 I/O model: the directory handle must be opened for overlapped I/O, the buffer must stay alive until completion, and the thread must enter an alertable wait so the completion routine can actually run.
The Core Setup
To use ReadDirectoryChangesW with a completion routine, you need all of these pieces:
- a directory handle opened with
FILE_FLAG_BACKUP_SEMANTICSandFILE_FLAG_OVERLAPPED - a buffer that remains valid until the asynchronous request finishes
- an
OVERLAPPEDstructure that also remains valid - a completion routine with the
CALLBACKsignature - an alertable wait such as
SleepEx(INFINITE, TRUE)
A stack buffer is usually the wrong choice because the function returns immediately while the I/O is still pending.
Open the Directory Correctly
First, open the directory handle with CreateFileW:
If that handle is invalid, nothing else will work. FILE_FLAG_BACKUP_SEMANTICS is required because you are opening a directory, not a normal file.
Keep Context with the OVERLAPPED
A convenient pattern is to store the OVERLAPPED structure and the notification buffer inside one context object:
This way, when the completion routine receives LPOVERLAPPED, you can recover the full watch state with CONTAINING_RECORD.
Queue the Asynchronous Read
Create a helper that starts or restarts monitoring:
Notice the key points:
- '
lpBytesReturnedisNULLfor overlapped use here' - the buffer belongs to
ctx, not a local variable - the same context can be re-armed after each notification batch
Process Notifications in the Completion Routine
When the I/O completes, Windows calls your completion routine on the same thread that entered the alertable wait:
Re-issuing ReadDirectoryChangesW at the end is what keeps the watcher alive.
The Alertable Wait Is Mandatory
This is the detail most examples skip: a completion routine does not run unless the thread enters an alertable wait state.
A minimal loop looks like this:
Without SleepEx or another alertable wait API, the callback never fires even though the directory changes are happening.
Common Pitfalls
The most common mistake is using a stack-allocated buffer or OVERLAPPED structure that goes out of scope while the I/O is still pending. The buffer and OVERLAPPED must live until completion.
Another frequent issue is forgetting FILE_FLAG_OVERLAPPED when opening the directory. Without it, you are not using asynchronous I/O correctly.
Developers also often forget the alertable wait requirement. A completion routine is not like a new worker thread. It runs only when the original thread enters an alertable wait state.
Buffer overflow is another practical problem. If the buffer is too small, Windows can report ERROR_NOTIFY_ENUM_DIR, and you may need to rescan the directory to rebuild state.
Finally, do not perform heavy work directly inside the completion routine. Parse the notifications, queue lighter work elsewhere if needed, and re-arm the watch promptly.
Summary
- Open the directory with
FILE_FLAG_BACKUP_SEMANTICSandFILE_FLAG_OVERLAPPED. - Keep the buffer and
OVERLAPPEDalive until the asynchronous request completes. - Use a completion routine only if the watching thread enters an alertable wait such as
SleepEx(INFINITE, TRUE). - Re-issue
ReadDirectoryChangesWafter handling each notification batch. - Plan for overflow and keep the completion routine lightweight.
Related reading
- How to use request_id while logging in asynchronous functions?
- How to use rolling update to re-pull container image?
- How to use skaffold with volumes
- How to use sudo inside a docker container?
- How to use RestSharp with async/await
- How to use SCNetworkReachability in Swift
- How to use the kubernetes go-client to get the same Pod status info that kubectl gives
- How to use variables with forward slash in kubernetes chart?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.