createfile — c ++ ReadDirectoryChangesW для каталога с символическими ссылками для каталогов

У меня есть приложение, которое я использую для отслеживания изменений файлов / каталогов в каталоге. Тем не менее, этот каталог также содержит symlinks в другие каталоги (которые доступны). Тем не менее, когда я изменяю file в пределах symlink каталог, уведомление не срабатывает. Например. когда я контролирую Root:

--Root
--Dir
--File1 //Trigger works
--SDir(Symlink dir)
--File2 //Trigger won't work

Тем не менее, когда я контролирую Root/SDir, который является каталогом символической ссылки, чем триггер на File2 работает правильно. Например.:

--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

Решение

Вот один из способов настроить отслеживание всего дерева … Это исключения из моей базы кода, я не могу поместить весь код здесь из-за зависимостей …

Использование:

   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

каст.

// *********************************************************************************************************************************
// *********************************************************************************************************************************

#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
0

Другие решения

Файловая система отправляет вам уведомление только тогда, когда файлы изменены внутри папки. символическая ссылка (или точка монтирования) — это только файл внутри папки, который указывает на другой файл / папку. если целевой файл / каталог не находится в нашей исходной папке — мы и не получим уведомление об изменениях внутри этой целевой папки

когда вы открываете прямую символическую ссылку (или точку монтирования) без 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);
}

моя реализация Защищенная защита (это не реализовано 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();
}
}
};
0

По вопросам рекламы [email protected]