Created
November 13, 2014 01:54
-
-
Save jasonwhite/86c751fb1b327a94ab8b to your computer and use it in GitHub Desktop.
Windows file monitoring implemented in C and D.
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
/* | |
* This is a short test program that prints a message when a file is added, | |
* removed, modified, or moved from any directory below that of the program's | |
* working directory. | |
*/ | |
#include <windows.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <tchar.h> | |
#include <stdint.h> | |
#include <memory.h> | |
void WatchDirectory(const char *dir); | |
void HandleChangeEvent(FILE_NOTIFY_INFORMATION *event); | |
int main(int argc, char **argv) | |
{ | |
char *cwd; | |
DWORD cwd_len; | |
cwd_len = GetCurrentDirectory(0, NULL); | |
if (!cwd_len) | |
perror("GetCurrentDirectory failed"); | |
cwd = (char*)malloc(cwd_len); | |
GetCurrentDirectory(cwd_len, cwd); | |
WatchDirectory(cwd); | |
return 0; | |
} | |
void WatchDirectory(const char *dir) | |
{ | |
uint8_t change_buf[1024]; | |
DWORD change_len; | |
HANDLE dirh; | |
BOOL succ; | |
FILE_NOTIFY_INFORMATION *event; | |
printf("Monitoring directory: %s\n", dir); | |
dirh = CreateFile(dir, | |
FILE_LIST_DIRECTORY, | |
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
NULL, | |
OPEN_EXISTING, | |
FILE_FLAG_BACKUP_SEMANTICS, | |
NULL); | |
if (dirh == INVALID_HANDLE_VALUE) | |
perror("Failed to open directory"); | |
for (;;) | |
{ | |
succ = ReadDirectoryChangesW( | |
dirh, change_buf, 1024, TRUE, | |
FILE_NOTIFY_CHANGE_FILE_NAME | | |
FILE_NOTIFY_CHANGE_DIR_NAME | | |
FILE_NOTIFY_CHANGE_LAST_WRITE, | |
&change_len, NULL, NULL | |
); | |
if (!succ) | |
perror("Failed to read directory changes"); | |
event = (FILE_NOTIFY_INFORMATION*)change_buf; | |
for (;;) | |
{ | |
HandleChangeEvent(event); | |
// Are there more events to handle? | |
if (event->NextEntryOffset) | |
*((uint8_t**)&event) += event->NextEntryOffset; | |
else | |
break; | |
} | |
} | |
} | |
void HandleChangeEvent(FILE_NOTIFY_INFORMATION* event) | |
{ | |
DWORD name_len; | |
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; | |
} | |
} |
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
/** | |
* Authors: Jason White | |
* | |
* Summary: | |
* Recursively monitors all directories under the current working directory. | |
*/ | |
import std.stdio, std.utf, std.path, std.typetuple, std.exception, std.file; | |
version (Windows) {} | |
else | |
{ | |
static assert(0, "Not implemented on this platform."); | |
} | |
import core.sys.windows.windows; | |
extern (Windows) | |
{ | |
alias LPOVERLAPPED_COMPLETION_ROUTINE = void function(DWORD, DWORD, OVERLAPPED*); | |
BOOL ReadDirectoryChangesW( | |
HANDLE hDirectory, | |
void* lpBuffer, | |
DWORD nBufferLength, | |
BOOL bWatchSubtree, | |
DWORD dwNotifyFilter, | |
DWORD* lpBytesReturned, | |
OVERLAPPED* lpOverlapped, | |
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine | |
); | |
struct FILE_NOTIFY_INFORMATION | |
{ | |
DWORD NextEntryOffset; | |
DWORD Action; | |
DWORD FileNameLength; | |
WCHAR FileName; | |
@property WCHAR[] filename() | |
{ | |
return (&FileName)[0 .. FileNameLength/WCHAR.sizeof]; | |
} | |
@property FILE_NOTIFY_INFORMATION *next() | |
{ | |
if (!NextEntryOffset) return null; | |
return cast(typeof(this)*)(cast(ubyte*)&NextEntryOffset + NextEntryOffset); | |
} | |
} | |
} | |
void watchDirectory(string dir) | |
{ | |
dir = buildNormalizedPath(dir); | |
writeln("Monitoring directory: ", dir); | |
alias defaults = TypeTuple!( | |
FILE_LIST_DIRECTORY, | |
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
(SECURITY_ATTRIBUTES*).init, | |
OPEN_EXISTING, | |
FILE_FLAG_BACKUP_SEMANTICS, | |
HANDLE.init | |
); | |
auto h = CreateFileW(toUTF16z(dir), defaults); | |
errnoEnforce(h != INVALID_HANDLE_VALUE, dir); | |
scope(exit) errnoEnforce(CloseHandle(h), dir); | |
ubyte[1024] changeBuf; | |
DWORD changeLen; | |
FILE_NOTIFY_INFORMATION *event; | |
while (true) | |
{ | |
errnoEnforce( | |
ReadDirectoryChangesW( | |
h, changeBuf.ptr, changeBuf.length, TRUE, | |
FILE_NOTIFY_CHANGE_FILE_NAME | | |
FILE_NOTIFY_CHANGE_DIR_NAME | | |
FILE_NOTIFY_CHANGE_LAST_WRITE, | |
&changeLen, | |
null, null | |
), "Failed to read directory changes" | |
); | |
event = cast(FILE_NOTIFY_INFORMATION*)changeBuf.ptr; | |
do | |
{ | |
handleChangeEvent(event); | |
event = event.next; | |
} while (event); | |
} | |
} | |
void handleChangeEvent(FILE_NOTIFY_INFORMATION* event) | |
{ | |
DWORD name_len = event.FileNameLength / WCHAR.sizeof; | |
string action; | |
final switch (event.Action) | |
{ | |
case FILE_ACTION_MODIFIED: | |
action = "Modified"; | |
break; | |
case FILE_ACTION_ADDED: | |
action = "Added"; | |
break; | |
case FILE_ACTION_REMOVED: | |
action = "Removed"; | |
break; | |
case FILE_ACTION_RENAMED_OLD_NAME: | |
action = "Renamed from"; | |
break; | |
case FILE_ACTION_RENAMED_NEW_NAME: | |
action = "to"; | |
break; | |
} | |
writefln("%12s: %s", action, event.filename); | |
} | |
void main() | |
{ | |
watchDirectory(getcwd()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment