Reputation: 49
I am trying to find whether a file is modified or not using FirstChangeNotification
in Windows using C++. Can I use FileSystemWatcher
class to do the same? Can anyone provide me with a solution for both ways if possible? I have searched and found snippets that I'm not able to understand since i am a beginner on these topics.
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
int main(int argc, char argv[])
{
DWORD dwWaitStatus;
HANDLE dwChangeHandles[2];
LPCWSTR DirName = L"F:\\myproject";
LPCWSTR DirName1 = L"F:\\";
dwChangeHandles[0] = FindFirstChangeNotification(
DirName,FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
ExitProcess(GetLastError());
else
printf("FindFirstChangeNotification() for file change is
OK.\n");
dwChangeHandles[1] = FindFirstChangeNotification(
DirName1,TRUE,FILE_NOTIFY_CHANGE_DIR_NAME);
if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)
{
printf("Something wrong!\n");
ExitProcess(GetLastError());
}
else
printf("FindFirstChangeNotification() for directory change is
OK.\n");
if (dwChangeHandles[0] != INVALID_HANDLE_VALUE &&
dwChangeHandles[1] != INVALID_HANDLE_VALUE)
{
printf("\nI'm monitoring any file deletion/creation in %S
and\n", DirName);
printf("I'm monitoring any directory deletion/creation in
%S.\n", DirName1);
}
while (TRUE)
{
dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE,
INFINITE);
switch (dwWaitStatus)
{
case 0:
if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)
{
printf("FindNextChangeNotification() not OK\n");
ExitProcess(GetLastError());
}
else
printf("File created/deleted in %S.\n", DirName);
break;
case 1:
if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)
ExitProcess(GetLastError());
else
printf("Directory was deleted/created in %S.\n",
DirName1);
break;
default:
printf("FindNextChangeNotification(): Invalid return
value.\n");
ExitProcess(GetLastError());
}
}
if(FindCloseChangeNotification(dwChangeHandles[0]) != 0)
printf("FindCloseChangeNotification() is OK\n");
if(FindCloseChangeNotification(dwChangeHandles[1]) != 0)
printf("FindCloseChangeNotification() is OK\n");
return 0;
}
The above code is in C and i have changed it to C++ and executed it.. It works well for file creation and deletion.. but when i use FILE_NOTIFY_CHANGE_LAST_WRITE
instead of FILE_NOTIFY_FILE_NAME
it prints
the statement twice.
Upvotes: 0
Views: 568
Reputation: 33804
most effective way do this - use ReadDirectoryChangesW
with asynchronous file handle. we create class, which encapsulate folder handle and all required info (say for example window handle, to where post notification on change). file handle we bind to system iocp via BindIoCompletionCallback
. as result every time when some modifications occur - our callback will be automatically called in system thread. here we can direct process changes or say post notify/data to gui thread. etc. after this, if no errors, callback again call ReadDirectoryChangesW
and so on. when we finally want stop monitoring - we can simply close file handle. as result current active i/o request will be completed with error ERROR_NOTIFY_CLEANUP
or if i/o request not active, when we close handle, we detect this and direct call callback with error code. when callback got error we stop monitoring and release object. with this we can create any count of monitor objects and continue execute another tasks. say gui thread - continue run message loop. for example:
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
NTSYSAPI
ULONG
__cdecl
DbgPrint (
_In_z_ _Printf_format_string_ PCSTR Format,
...
);
NTSYSAPI
ULONG
NTAPI
RtlNtStatusToDosError (
_In_ NTSTATUS Status
);
#ifdef __cplusplus
}
#endif
class CMonitor
{
friend struct NDC_IRP;
HANDLE _hFile;
DWORD _dwNotifyFilter;
LONG _nRef;
LONG _HandleLock;
BOOL LockHandle();
void UnlockHandle();
~CMonitor()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
Close();
}
BOOL OnComplete(NDC_IRP* Irp, DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered, PFILE_NOTIFY_INFORMATION pfni );
public:
CMonitor(DWORD dwNotifyFilter) : _nRef(1), _HandleLock(0), _hFile(0)
{
_dwNotifyFilter = dwNotifyFilter;
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
void AddRef()
{
InterlockedIncrement(&_nRef);
}
void Release()
{
if (!InterlockedDecrement(&_nRef)) delete this;
}
void Assign(HANDLE hFile)
{
_hFile = hFile, _HandleLock = 0x80000000;
}
void Close();
ULONG Open(PCWSTR FileName);
ULONG DoRead();
ULONG DoRead(NDC_IRP* irp);
static void DumpDirectoryChanges(PVOID pv);
};
struct NDC_IRP : OVERLAPPED
{
CMonitor* _pObj;
union {
FILE_NOTIFY_INFORMATION _fni;
BYTE _buf[0x1000];// aligned as FILE_NOTIFY_INFORMATION
};
NDC_IRP(CMonitor* pObj) : _pObj(pObj)
{
pObj->AddRef();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~NDC_IRP()
{
_pObj->Release();
}
VOID IOCompletionRoutine(DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered)
{
if (_pObj->OnComplete(this, dwErrorCode, dwNumberOfBytesTransfered, &_fni))
{
delete this;
}
}
static VOID CALLBACK _IOCompletionRoutine(ULONG status, ULONG dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<NDC_IRP*>(lpOverlapped)->IOCompletionRoutine(RtlNtStatusToDosError(status), dwNumberOfBytesTransfered);
}
static ULONG Bind(HANDLE hFile)
{
return BindIoCompletionCallback(hFile, _IOCompletionRoutine, 0) ? NOERROR : GetLastError();
}
DWORD CheckErrorCode(DWORD dwErrorCode)
{
switch (dwErrorCode)
{
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
IOCompletionRoutine(dwErrorCode, 0);
}
return ERROR_IO_PENDING;
}
};
//////////////////////////////////////////////////////////////////////////
// CMonitor
void CMonitor::Close()
{
if (LockHandle())
{
_interlockedbittestandreset(&_HandleLock, 31);
UnlockHandle();
}
}
BOOL CMonitor::LockHandle()
{
LONG Value = _HandleLock, NewValue;
for ( ; Value < 0; Value = NewValue)
{
NewValue = _InterlockedCompareExchange(&_HandleLock, Value + 1, Value);
if (NewValue == Value) return TRUE;
}
return FALSE;
}
void CMonitor::UnlockHandle()
{
if (!_InterlockedDecrement(&_HandleLock))
{
CloseHandle(_hFile);
_hFile = 0;
}
}
ULONG CMonitor::DoRead()
{
if (NDC_IRP* irp = new NDC_IRP(this))
{
return DoRead(irp);
}
return ERROR_NO_SYSTEM_RESOURCES;
}
ULONG CMonitor::DoRead(NDC_IRP* irp)
{
ULONG err = ERROR_INVALID_HANDLE;
if (LockHandle())
{
err = ReadDirectoryChangesW(_hFile,
irp->_buf, sizeof(irp->_buf), TRUE, _dwNotifyFilter, 0, irp, 0)
? ERROR_IO_PENDING : GetLastError();
UnlockHandle();
}
irp->CheckErrorCode(err);
return NOERROR;
}
ULONG CMonitor::Open(PCWSTR FileName)
{
HANDLE hFile = CreateFile(FileName, FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
if (ULONG err = NDC_IRP::Bind(hFile))
{
return err;
}
Assign(hFile);
return NOERROR;
}
BOOL CMonitor::OnComplete(NDC_IRP* irp, DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered, PFILE_NOTIFY_INFORMATION pfni )
{
switch (dwErrorCode)
{
case ERROR_NOTIFY_CLEANUP:
DbgPrint("%p> ---- NOTIFY_CLEANUP -----\n", this);
break;
case ERROR_NOTIFY_ENUM_DIR:
DbgPrint("%p> ---- ERROR_NOTIFY_ENUM_DIR -----\n", this);
case NOERROR:
if (dwNumberOfBytesTransfered)
{
DumpDirectoryChanges(pfni);
}
DoRead(irp);
return FALSE;// reuse irp
default:
DbgPrint("%p> error=%x\n", this, dwErrorCode);
}
return TRUE;// free irp
}
void CMonitor::DumpDirectoryChanges(PVOID pv)
{
union {
PVOID buf;
PBYTE pb;
PFILE_NOTIFY_INFORMATION pfni;
};
buf = pv;
for (;;)
{
DbgPrint("%x <%.*S>\n", pfni->Action, pfni->FileNameLength >> 1, pfni->FileName);
ULONG NextEntryOffset = pfni->NextEntryOffset;
if (!NextEntryOffset)
{
break;
}
pb += NextEntryOffset;
}
}
#define FILE_NOTIFY_VALID_MASK 0x00000fff
void CloseMonitor(CMonitor*p)
{
p->Close();
p->Release();
}
ULONG CreateMonitor(CMonitor** ppvObj, PCWSTR FileName, DWORD dwNotifyFilter)
{
if (CMonitor* p = new CMonitor(dwNotifyFilter))
{
ULONG err;
if (!(err = p->Open(FileName)) && !(err = p->DoRead()))
{
*ppvObj = p;
return NOERROR;
}
CloseMonitor(p);
return err;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
void demo()
{
CMonitor *p, *q;
if (!CreateMonitor(&p, L"c:\\", FILE_NOTIFY_VALID_MASK))
{
if (!CreateMonitor(&q, L"d:\\", FILE_NOTIFY_VALID_MASK))
{
MessageBoxW(0, 0, L"monitoring..", MB_ICONINFORMATION);
CloseMonitor(q);
}
CloseMonitor(p);
}
}
Upvotes: 4