Last active
October 3, 2024 03:51
-
-
Save nickav/a57009d4fcc3b527ed0f5c9cf30618f8 to your computer and use it in GitHub Desktop.
Example of how to poll ReadDirectoryChangesW on Windows
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main() { | |
char *path = "/path/to/my/directory"; | |
print("watching %s for changes...\n", path); | |
HANDLE file = CreateFile(path, | |
FILE_LIST_DIRECTORY, | |
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
NULL, | |
OPEN_EXISTING, | |
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, | |
NULL); | |
assert(file != INVALID_HANDLE_VALUE); | |
OVERLAPPED overlapped; | |
overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); | |
uint8_t change_buf[1024]; | |
BOOL success = ReadDirectoryChangesW( | |
file, change_buf, 1024, TRUE, | |
FILE_NOTIFY_CHANGE_FILE_NAME | | |
FILE_NOTIFY_CHANGE_DIR_NAME | | |
FILE_NOTIFY_CHANGE_LAST_WRITE, | |
NULL, &overlapped, NULL); | |
while (true) { | |
DWORD result = WaitForSingleObject(overlapped.hEvent, 0); | |
if (result == WAIT_OBJECT_0) { | |
DWORD bytes_transferred; | |
GetOverlappedResult(file, &overlapped, &bytes_transferred, FALSE); | |
FILE_NOTIFY_INFORMATION *event = (FILE_NOTIFY_INFORMATION*)change_buf; | |
for (;;) { | |
DWORD name_len = event->FileNameLength / sizeof(wchar_t); | |
switch (event->Action) { | |
case FILE_ACTION_ADDED: { | |
wprintf(L" Added: %.*s\n", name_len, event->FileName); | |
} break; | |
case FILE_ACTION_REMOVED: { | |
wprintf(L" Removed: %.*s\n", name_len, event->FileName); | |
} break; | |
case FILE_ACTION_MODIFIED: { | |
wprintf(L" Modified: %.*s\n", name_len, event->FileName); | |
} break; | |
case FILE_ACTION_RENAMED_OLD_NAME: { | |
wprintf(L"Renamed from: %.*s\n", name_len, event->FileName); | |
} break; | |
case FILE_ACTION_RENAMED_NEW_NAME: { | |
wprintf(L" to: %.*s\n", name_len, event->FileName); | |
} break; | |
default: { | |
printf("Unknown action!\n"); | |
} break; | |
} | |
// Are there more events to handle? | |
if (event->NextEntryOffset) { | |
*((uint8_t**)&event) += event->NextEntryOffset; | |
} else { | |
break; | |
} | |
} | |
// Queue the next event | |
BOOL success = ReadDirectoryChangesW( | |
file, change_buf, 1024, TRUE, | |
FILE_NOTIFY_CHANGE_FILE_NAME | | |
FILE_NOTIFY_CHANGE_DIR_NAME | | |
FILE_NOTIFY_CHANGE_LAST_WRITE, | |
NULL, &overlapped, NULL); | |
} | |
// Do other loop stuff here... | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Something additional to note: the change_buf needs to be DWORD aligned, as stated in the docs: "A pointer to the DWORD-aligned formatted buffer in which the read results are to be returned". Power-of-2 alignments greater than alignof(DWORD) should also work. For example, malloc always returns memory locations that are 16-byte aligned (or a similarly large alignment).
We can use the C++11 alignas specifier for aligning a local buffer:
alignas(DWORD) uint8_t change_buf[1024];
Edit: there's an additional alignment problem that should be noted. It seems that Windows will sometimes give you values for
NextEntryOffset
that if you directly castFILE_NOTIFY_INFORMATION
from theu8
buffer at that point, it will be a misaligned-memory read. This probably will still be valid on your CPU - but with a speed penalty, or it might be a crash. If you have Clang/GCC Undefined Behavior Sanitizer enabled you may get a crash here. The workaround for this problem is tomemcpy
the data out of the buffer into anotherFILE_NOTIFY_INFORMATION
which you know will be aligned correctly. Be careful with the FileName member while doing this - you probably need to do a separatememcpy
for that;offsetof(FILE_NOTIFY_INFORMATION, FileName)
may be helpful here to determine the memory location to copy from.