Skip to content

Instantly share code, notes, and snippets.

@jasonwhite
Created November 13, 2014 01:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonwhite/86c751fb1b327a94ab8b to your computer and use it in GitHub Desktop.
Save jasonwhite/86c751fb1b327a94ab8b to your computer and use it in GitHub Desktop.
Windows file monitoring implemented in C and D.
/*
* 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;
}
}
/**
* 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