Just find a attractive API unexpectedly, ReadDirectoryChangesW can be used to watch a directory. Whenever there occurs an operation on/inside it, we can get notified with information of the operation which file/subdirectory is on, and even their names. Someone on the CodeProject has already attached a wrapped library to do the same thing, with calling ReadDirectoryChangesW under the hood. 🙂
After survey the MSDN, I also found another 3 API group used to watch a directory: FindFirstChangeNotification, FindNextChangeNotification, and FindCloseChangeNotification. The shorti coming of these 3 APIs is that they seem not be able to get the enough information on what happened there.
Below is the simplest demo code used to watch a directory, I use it in a synchronous way. (Some advanced usage of it will involve IO Completion Routine, but since it is too small here, I don’t think IO Completion is suitable for my demo code here).
#include "stdafx.h"
#include "windows.h"
void DumpChangesInfo(const PFILE_NOTIFY_INFORMATION fni);
void StartWatchDirectory(const TCHAR * strDirectoryName);
int _tmain(int argc, _TCHAR* argv[])
{
if (argc != 2)
return 0;
StartWatchDirectory(argv[1]);
return 0;
}
void StartWatchDirectory(const TCHAR * strDirectoryName)
{
HANDLE hDirectory;
hDirectory = CreateFile(
strDirectoryName, // Directory name/path
FILE_LIST_DIRECTORY | FILE_TRAVERSE | GENERIC_READ, // Desired Access
FILE_SHARE_WRITE | FILE_SHARE_READ, // File shared mode
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, // Flags and Attributes
NULL
);
if (hDirectory == INVALID_HANDLE_VALUE)
{
printf("Error Code[%d]: Open Directory Failed…\n", GetLastError());
return;
}
BYTE buffer[1024] = { 0 }; // hardcode the length of the buffer, ReadDirectoryChangesW may return 0 in dwBytesRet.
BOOL bResult = FALSE;
FILE_NOTIFY_INFORMATION * fni = NULL;
DWORD dwBufferLen = 0;
DWORD dwBytesRet = 0;
DWORD dwNotifyFilter =
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY; // Notify for every operations
printf("Directory Changes is starting…\n");
do
{
ZeroMemory(&buffer, 1024);
bResult = ReadDirectoryChangesW(
hDirectory, // Directory handle
(LPVOID)&buffer, // Buffer
1024, // Length of the buffer
TRUE, // If watch sub directory
dwNotifyFilter, // NotifyFilter
&dwBytesRet, // Number of Byte returned
NULL, // OVERLAPPED structure
NULL // IO Completion Routine
);
if (bResult == 0)
{
printf("Error Code[%d]: Read Directory Changes Failed…\n", GetLastError());
break;
}
fni = (PFILE_NOTIFY_INFORMATION)&buffer;
DumpChangesInfo(fni);
} while(bResult);
printf("Directory Watcher is shutting down…\n");
CloseHandle(hDirectory);
}
void DumpChangesInfo(const PFILE_NOTIFY_INFORMATION fni)
{
// FileNameLength returned in PFILE_NOTIFY_INFORMATION is a WCHAR * without ‘0\’ as its termination.
WCHAR * strFileName = (WCHAR * )malloc(fni->FileNameLength + sizeof(WCHAR));
wcscpy(strFileName, fni->FileName);
strFileName[fni->FileNameLength/sizeof(WCHAR)] = _T(”);
TCHAR strAction[20];
memset(&strAction, ”, 20 * sizeof(TCHAR));
switch (fni->Action)
{
case FILE_ACTION_ADDED:
_tcscpy(strAction, _T("ADD"));
break;
case FILE_ACTION_REMOVED:
_tcscpy(strAction, _T("REMOVED"));
break;
case FILE_ACTION_MODIFIED:
_tcscpy(strAction, _T("MODIFIED"));
break;
case FILE_ACTION_RENAMED_OLD_NAME:
_tcscpy(strAction, _T("RENAMED"));
break;
case FILE_ACTION_RENAMED_NEW_NAME:
_tcscpy(strAction, _T("RENAMED"));
break;
}
_tprintf(_T("\tFile/Directory:\t%s -> %s\n"), strFileName, strAction);
free(strFileName);
}
[Update] Just a minute ago, I found another guy posted a more object-oriented sample code >”<
http://blog.vckbase.com/bruceteen/articles/229.html
Wow, I was wondering how Windows Media Player watched a media folder changes to update its media content, it maybe also use this ReadDirectoryChanges to do that.
Helpful.