我试图在使用C ++的Windows中使用FirstChangeNotification
来查找是否修改了文件。我可以使用FileSystemWatcher
类来做同样的事吗?如果可能,任何人都可以为我提供两种解决方案吗?我搜索并找到了我无法理解的片段,因为我是这些主题的初学者。
#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
时,它会打印出来
声明两次。
答案 0 :(得分:4)
最有效的方法 - 将ReadDirectoryChangesW
与异步文件句柄一起使用。我们创建了一个类,它封装了文件夹句柄和所有必需的信息(例如窗口句柄,以及更改后发布通知的位置)。文件句柄我们通过BindIoCompletionCallback
绑定到系统iocp。每当发生一些修改时结果 - 我们的回调将在系统线程中自动调用。在这里我们可以直接进行流程更改或者将通知/数据发布到gui线程。等等,此后,如果没有错误,再次回拨呼叫ReadDirectoryChangesW
等等。当我们最终想要停止监控时 - 我们可以简单地关闭文件句柄。结果当前活动的i / o请求将以错误ERROR_NOTIFY_CLEANUP
完成,或者如果i / o请求未激活,当我们关闭句柄时,我们检测到这一点并使用错误代码直接调用回调。当回调出错时,我们停止监视并释放对象。有了这个,我们可以创建任何计数的监视器对象,并继续执行其他任务。说gui线程 - 继续运行消息循环。例如:
#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);
}
}