-
-
Save nickav/a57009d4fcc3b527ed0f5c9cf30618f8 to your computer and use it in GitHub Desktop.
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... | |
} | |
} |
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 cast FILE_NOTIFY_INFORMATION
from the u8
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 to memcpy
the data out of the buffer into another FILE_NOTIFY_INFORMATION
which you know will be aligned correctly. Be careful with the FileName member while doing this - you probably need to do a separate memcpy
for that; offsetof(FILE_NOTIFY_INFORMATION, FileName)
may be helpful here to determine the memory location to copy from.
thanks