目录上的c ++ ReadDirectoryChangesW,目录为

时间:2017-07-22 21:32:41

标签: c++ createfile readdirectorychangesw

我有一个应用程序,用于监视目录中的文件/目录更改。但是,此目录还包含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的根,为什么触发器不会对符号链接中的文件起作用?

2 个答案:

答案 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();
        }
    }
};