我有一个应用程序,用于监视目录中的文件/目录更改。但是,此目录还包含symlinks
到其他目录(可访问)。但是,当我修改file
目录中的symlink
时,通知 不 已触发。例如。当我监控Root
时:
--Root
--Dir
--File1 //Trigger works
--SDir(Symlink dir)
--File2 //Trigger won't work
但是,当我监视Root/SDir
时,这是symlink目录,而不是File2上的触发器正常工作。 E.g:
--SDir
--File2 //Trigger works
因此,当symlink directory
不是根时,它不会触发该目录中的文件更改。但是,当我将symlink directory
设置为根时,它可以正常工作。是的,bWatchSubtree
中的ReadDirectoryChangesW-function
参数设置为true
。
为了顺利,我使用CreateFile
函数打开目录句柄:
CreateFile(
Dir, //Root or Root/SDir in this example
FILE_LIST_DIRECTORY,
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
那么,如果相同的符号链接不是ReadDirectoryChangesW
的根,为什么触发器不会对符号链接中的文件起作用?
答案 0 :(得分:0)
这是设置监视整个树的一种方法......这些是我的代码库中的例外,我不能把整个代码放在这里因为依赖...
用法:
FolderWatchInfo fwi;
fwi.Create(strRootPath, TRUE,
FolderWatchInfo::flagDirWatch | FolderWatchInfo::flagFileWatch);
WaitForSingleObject(fwi.GetHandle(), INFINITE); // or the usual WaitForMultipleObjects(...)
// with an exit event
//...
//On event signaled:
CStringsVector vFolders;
fwi.GetSubDirChangeList(vFolders);
Sleep(1000); // give a little time for things to settle...
for each (const CIRString& str in vFolders)
{
OnFolderChange(str); // signal my app
}
事件是自动重置的,因此无需维护。
那不存在的东西:
CIRSingleLock就像lock_guard
DirScanner是FindFirstFile()的包装器,它将文件夹报告给Reader结构。
包含文件:
// *********************************************************************************************************************************
// *********************************************************************************************************************************
#pragma once
#include <DirScanner.h>
#include <shared_ptr.h>
#include <boost/weak_ptr.hpp>
#include <vector>
#include <IRSingleLock.h>
// *********************************************************************************************************************************
// *********************************************************************************************************************************
class FolderWatchInfo
{
// --------------------------------------------------------------------------------------------------------------------------------
public:
enum Flags
{
flagFileCreateDelete = FILE_NOTIFY_CHANGE_FILE_NAME,
flagFileModify = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
flagFileSecurity = FILE_NOTIFY_CHANGE_SECURITY,
flagFileAttributes = FILE_NOTIFY_CHANGE_ATTRIBUTES,
flagDirCreateDetelete = FILE_NOTIFY_CHANGE_DIR_NAME,
flagFileWatch = flagFileCreateDelete | flagFileModify,
flagDirWatch = flagDirCreateDetelete,
};
// --------------------------------------------------------------------------------------------------------------------------------
typedef shared_ptr<FolderWatchInfo> FolderWatchInfoPtr;
typedef std::vector<FolderWatchInfoPtr> FWIVector;
typedef std::vector<CIRString> CStringsVector;
// --------------------------------------------------------------------------------------------------------------------------------
private:
struct Reader : public DirScanner::Reader
{
FolderWatchInfo& fwi;
Reader(FolderWatchInfo& fwi_)
: DirScanner::Reader(fwi_.GetPathName(), _T("__NOFILES__"), 1)
, fwi(fwi_)
{ }
virtual bool OnDirectoryFound(LPCTSTR szPathName, const WIN32_FIND_DATA& findData)
{
FolderWatchInfoPtr p = make_shared<FolderWatchInfo>();
p->Create(fwi.GetPathName() + _T("\\") + szPathName, fwi.GetFlags());
fwi.m_vChildren.push_back(p);
return TRUE;
}
};
friend struct Reader;
private:
CIRCriticalSection m_lock;
weak_ptr<FolderWatchInfo> m_pParent;
CIRString m_strPathName;
HANDLE m_hWatchEvent;
DWORD m_dwFlags;
FWIVector m_vChildren;
// --------------------------------------------------------------------------------------------------------------------------------
private:
inline FolderWatchInfo(const FolderWatchInfo&) {}
inline bool operator = (const FolderWatchInfo&) {}
// --------------------------------------------------------------------------------------------------------------------------------
public:
FolderWatchInfo();
~FolderWatchInfo();
// --------------------------------------------------------------------------------------------------------------------------------
HANDLE Create(const CIRString& strPathName, BOOL bRecursive, DWORD dwFlags);
void CloseHandle();
void ResetEvent();
protected:
HANDLE Create(const CIRString& strPathName, DWORD dwFlags);
void AddToSubDirChangeList(CStringsVector& vSubDirs);
// --------------------------------------------------------------------------------------------------------------------------------
public:
inline HANDLE GetHandle() const
{
return m_hWatchEvent;
}
// --------------------------------------------------------------------------------------------------------------------------------
inline void GetSubDirChangeList(CStringsVector& vSubDirs)
{
CIRSingleLock lock(m_lock);
vSubDirs.clear();
AddToSubDirChangeList(vSubDirs);
}
// --------------------------------------------------------------------------------------------------------------------------------
inline FolderWatchInfoPtr GetParent() const
{
return FolderWatchInfoPtr(m_pParent, boost::detail::sp_nothrow_tag());
}
// --------------------------------------------------------------------------------------------------------------------------------
inline const FWIVector& GetChildren() const
{
return m_vChildren;
}
// --------------------------------------------------------------------------------------------------------------------------------
inline const CIRString& GetPathName() const
{
return m_strPathName;
}
// --------------------------------------------------------------------------------------------------------------------------------
inline DWORD GetFlags() const
{
return m_dwFlags;
}
// --------------------------------------------------------------------------------------------------------------------------------
inline bool operator == (LPCTSTR szPath)
{
return (m_strPathName.CompareNoCase(szPath) == 0);
}
};
// ***************************************************************************************************************************** EOF
CPP。
// *********************************************************************************************************************************
// *********************************************************************************************************************************
#include "StdAfx.h"
#include "FolderWatchInfo.h"
// *********************************************************************************************************************************
FolderWatchInfo::FolderWatchInfo()
: m_hWatchEvent(INVALID_HANDLE_VALUE)
, m_dwFlags(0)
{ }
// *********************************************************************************************************************************
FolderWatchInfo::~FolderWatchInfo(void)
{
CIRSingleLock lock(m_lock);
if (m_hWatchEvent != INVALID_HANDLE_VALUE)
{
FindCloseChangeNotification(m_hWatchEvent);
}
}
// *********************************************************************************************************************************
HANDLE FolderWatchInfo::Create(const CIRString& strPathName, BOOL bRecursive, DWORD dwFlags)
{
CIRSingleLock lock(m_lock);
ASSERT(m_hWatchEvent == INVALID_HANDLE_VALUE);
m_strPathName = CleanPathName(strPathName);
m_dwFlags = dwFlags;
m_hWatchEvent = FindFirstChangeNotification(m_strPathName, bRecursive, dwFlags);
if (bRecursive)
{
DirScanner().ScanFolder(Reader(*this));
}
return m_hWatchEvent;
}
// *********************************************************************************************************************************
HANDLE FolderWatchInfo::Create(const CIRString& strPathName, DWORD dwFlags)
{
CIRSingleLock lock(m_lock);
ASSERT(m_hWatchEvent == INVALID_HANDLE_VALUE);
m_strPathName = CleanPathName(strPathName);
m_dwFlags = dwFlags;
m_hWatchEvent = FindFirstChangeNotification(m_strPathName, FALSE, dwFlags);
DirScanner().ScanFolder(Reader(*this));
return m_hWatchEvent;
}
// *********************************************************************************************************************************
void FolderWatchInfo::CloseHandle()
{
CIRSingleLock lock(m_lock);
if (m_hWatchEvent != INVALID_HANDLE_VALUE)
{
FindCloseChangeNotification(m_hWatchEvent);
m_hWatchEvent = INVALID_HANDLE_VALUE;
}
}
// *********************************************************************************************************************************
void FolderWatchInfo::ResetEvent()
{
if (m_hWatchEvent != INVALID_HANDLE_VALUE)
{
while (WAIT_OBJECT_0 == WaitForSingleObject(m_hWatchEvent, 0))
{
if (!FindNextChangeNotification(m_hWatchEvent))
{
CloseHandle();
break;
}
}
}
}
// *********************************************************************************************************************************
void FolderWatchInfo::AddToSubDirChangeList(CStringsVector& vSubDirs)
{
CIRSingleLock lock(m_lock);
if (WAIT_OBJECT_0 == WaitForSingleObject(m_hWatchEvent, 0))
{
ResetEvent();
vSubDirs.push_back(m_strPathName);
}
for each (const FolderWatchInfoPtr& p in m_vChildren)
{
p->AddToSubDirChangeList(vSubDirs);
}
}
// ***************************************************************************************************************************** EOF
答案 1 :(得分:0)
filesystem仅在文件夹内更改文件时发送通知。符号链接(或挂载点)这只是文件夹内的文件,指向另一个文件/文件夹。如果目标文件/目录不在我们的原始文件夹中 - 我们并没有获得有关此目标文件夹内的更改的通知
当你打开没有FILE_FLAG_OPEN_REPARSE_POINT
的直接符号链接(或挂载点)时,你真的打开了目标目录(这个符号链接到哪一点)并从这个目录中获得通知
从某个文件夹获取通知通常需要做下一步:
FILE_FLAG_OVERLAPPED
打开目录句柄进行处理
ReadDirectoryChangesW
异步BindIoCompletionCallback
打开句柄 - 结果
当来自文件系统的通知时,将调用回调。ReadDirectoryChangesW
并且下次从回调中调用它(通过注册
BindIoCompletionCallback
)直到我们没有
ERROR_NOTIFY_CLEANUP
ERROR_NOTIFY_CLEANUP
使用这个类我们可以拥有任何目录间谍数 - 只需要创建这个类的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);
}
我对rundown protection的实现(在用户模式下没有由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();
}
}
};