只有当文件夹内的文件发生更改时,文件系统才会向您发送通知。符号链接(或挂载点)这只是文件夹内的文件,它指向另一个文件/文件夹。如果目标文件/目录不在我们的原始文件夹内 - 我们不会收到有关此目标文件夹内更改的通知
当您打开直接符号链接(或挂载点)而没有FILE_FLAG_OPEN_REPARSE_POINT
真正打开目标目录(此符号链接到该点)并从该目录获得通知时
从某个文件夹收到通知通常需要下一步:
有了这个类,我们可以有任意数量的目录间谍——只需要创建这个类的 N 个实例。无需等待、循环等。只需继续执行您的任务。当您不再需要来自目录的通知时 - 关闭它处理并释放结构
最简单的实现:
class SPYDATA : public OVERLAPPED, RUNDOWN_REF
{
HANDLE m_hFile;
DWORD _dwNotifyFilter;
LONG _dwRef;
UCHAR _buf[PAGE_SIZE];
~SPYDATA()
{
Close();
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
void DumpDirectoryChanges()
{
union {
PVOID buf;
PBYTE pb;
PFILE_NOTIFY_INFORMATION pfni;
};
buf = _buf;
for (;;)
{
DbgPrint("[%03x] %x <%.*S>\n", _dwNotifyFilter, pfni->Action, pfni->FileNameLength >> 1, pfni->FileName);
ULONG NextEntryOffset = pfni->NextEntryOffset;
if (!NextEntryOffset)
{
break;
}
pb += NextEntryOffset;
}
}
void IOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)
{
switch (dwErrorCode)
{
case ERROR_NOTIFY_CLEANUP:
DbgPrint("%p>[%x] ---- NOTIFY_CLEANUP -----\n", this, _dwNotifyFilter);
return ;
case NOERROR:
if (dwNumberOfBytesTransfered) DumpDirectoryChanges();
DoRead();
return;
}
DbgPrint("%p>[%x] error=%x\n", this, _dwNotifyFilter, dwErrorCode);
}
void IOCompletionRoutineAndRelease(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)
{
IOCompletionRoutine(dwErrorCode, dwNumberOfBytesTransfered);
Release();
}
static VOID CALLBACK _IOCompletionRoutine(
__in DWORD status,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped
)
{
static_cast<SPYDATA*>(lpOverlapped)->IOCompletionRoutineAndRelease(RtlNtStatusToDosError(status), dwNumberOfBytesTransfered);
}
virtual void RundownCompleted()
{
if (m_hFile) CloseHandle(m_hFile);
}
public:
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
SPYDATA(DWORD dwNotifyFilter) : _dwNotifyFilter(dwNotifyFilter)
{
_dwRef = 1;
m_hFile = 0;
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
void DoRead()
{
if (AcquireRundownProtection())
{
AddRef();
ULONG dwErrorCode = ReadDirectoryChangesW(m_hFile, _buf, sizeof(_buf),
TRUE, _dwNotifyFilter, NULL, this, NULL) ? NOERROR : GetLastError();
ReleaseRundownProtection();
switch (dwErrorCode)
{
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
IOCompletionRoutineAndRelease(dwErrorCode, 0);
}
}
}
ULONG Open(PCWSTR lpFileName )
{
HANDLE hFile = CreateFile(lpFileName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
if (BindIoCompletionCallback(hFile, _IOCompletionRoutine, 0))
{
m_hFile = hFile;
return NOERROR;
}
CloseHandle(hFile);
}
return GetLastError();
}
void Close()
{
BeginRundown();
}
};
BOOL CreateSpy(DWORD dwNotifyFilter, PCWSTR lpFileName, SPYDATA** pp)
{
if (SPYDATA* p = new SPYDATA(dwNotifyFilter))
{
ULONG dwError = p->Open(lpFileName);
if (!dwError)
{
*pp = p;
p->DoRead();
return NOERROR;
}
p->Release();
return dwError;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
void DestroySpyData(SPYDATA* p)
{
if (p)
{
p->Close();
p->Release();
}
}
并将其用作:
SPYDATA* p;
if (!CreateSpy(FILE_NOTIFY_VALID_MASK, L"<some path>", &p))
{
MessageBoxW(0,0,0,0);// really here any code
DestroySpyData(p);
}
我对破旧保护的实现(它不是由用户模式下的 api 实现的)是
#define RUNDOWN_INIT_VALUE 0x80000000
#define RUNDOWN_COMPLETE_VALUE 0
#define ObpBeginRundown(p) _interlockedbittestandreset(p, 31)
#define ObpUnlock _InterlockedDecrement
__forceinline BOOL ObpLock(PLONG pLock)
{
LONG Value = *pLock, NewValue;
for ( ; Value; Value = NewValue)
{
NewValue = _InterlockedCompareExchange(pLock, Value + 1, Value);
if (NewValue == Value) return TRUE;
}
return FALSE;
}
__forceinline BOOL ObpAcquireRundownProtection(PLONG pLock)
{
LONG Value = *pLock, NewValue;
for ( ; Value < 0; Value = NewValue)
{
NewValue = _InterlockedCompareExchange(pLock, Value + 1, Value);
if (NewValue == Value) return TRUE;
}
return FALSE;
}
class __declspec(novtable) RUNDOWN_REF
{
LONG _LockCount;
protected:
virtual void RundownCompleted() = 0;
public:
BOOL IsRundownBegin()
{
return 0 <= _LockCount;
}
__forceinline void Reinit()
{
if (InterlockedCompareExchange(&_LockCount, RUNDOWN_INIT_VALUE, RUNDOWN_COMPLETE_VALUE) != RUNDOWN_COMPLETE_VALUE)
{
__debugbreak();
}
}
__forceinline RUNDOWN_REF()
{
_LockCount = RUNDOWN_INIT_VALUE;
}
__forceinline BOOL AcquireRundownProtection()
{
return ObpAcquireRundownProtection(&_LockCount);
}
__forceinline void ReleaseRundownProtection()
{
if (!_InterlockedDecrement(&_LockCount))
{
RundownCompleted();
}
}
void BeginRundown()
{
if (AcquireRundownProtection())
{
ObpBeginRundown(&_LockCount);
ReleaseRundownProtection();
}
}
};