问题描述
我正在尝试使用 C++ 在 Windows 中使用 FirstChangeNotification
查找文件是否被修改.我可以使用 FileSystemWatcher
类来做同样的事情吗?如果可能,任何人都可以为我提供两种方式的解决方案吗?我搜索并发现了一些我无法理解的片段,因为我是这些主题的初学者.
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;
}
上面的代码是用 C 写的,我已经把它改成 C++ 并执行了..它适用于文件创建和删除..但是当我使用 FILE_NOTIFY_CHANGE_LAST_WRITE
而不是 FILE_NOTIFY_FILE_NAME
它打印声明两次.
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 printsthe statement twice.
推荐答案
最有效的方法 - 使用 ReadDirectoryChangesW
和 异步 文件句柄.我们创建了类,它封装了文件夹句柄和所有必需的信息(例如窗口句柄,在更改时发布通知的位置).我们通过 BindIoCompletionCallback
绑定到系统 iocp 的文件句柄.结果每次发生一些修改时 - 我们的回调将在系统线程中自动调用.在这里,我们可以直接更改进程或说将通知/数据发布到 gui 线程.等等之后,如果没有错误,再次回调调用ReadDirectoryChangesW
等等.当我们最终想要停止监控时 - 我们可以简单地关闭文件句柄.结果当前活动的 i/o 请求将完成并出现错误 ERROR_NOTIFY_CLEANUP
或者如果 i/o 请求未激活,当我们关闭句柄时,我们会检测到这一点并使用错误代码直接调用回调.当回调出错时,我们停止监视并释放对象.有了这个,我们可以创建任意数量的监视器对象并继续执行其他任务.说 gui 线程 - 继续运行消息循环.例如:
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);
}
}
这篇关于如何使用 C++ 在 Windows 中查找文件是否被修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!