// ==========================================================================
// Class Implementation : COXFileWatcher
// ==========================================================================
// Source file : OXFileWatcher.cpp
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "OXFileWatcher.h"
#include "OXMainRes.h"
// v9.3 - update 03 - 64-bit - included for possible redef of PtrToLong
#include "UTB64Bit.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
#ifdef _DEBUG
// Trace a message when the RESULT specifies failure
#define CONDITIONAL_TRACE_RESULT(TEXT, RESULT) \
{ if (FAILED(RESULT)) { \
TRACE(_T("%s : Failed (%u == 0x%X, Code : %u) :\n\t%s\n"), \
_T(TEXT), RESULT, RESULT, HRESULT_CODE(RESULT), COXFileWatcher::GetResultMessage(RESULT)); \
} }
#else
// Do not trace in Release build
#define CONDITIONAL_TRACE_RESULT(TEXT, RESULT)
#endif // _DEBUG
/////////////////////////////////////////////////////////////////////////////
// COXFileWatcher
// Data members -------------------------------------------------------------
// public:
/////////////////////////////////////////////////////////////////////////////
// Definition of static members
const DWORD COXFileWatcher::OXFileWatchChangeFileName = FILE_NOTIFY_CHANGE_FILE_NAME;
// --- Any filename change in the watched directory or subtree causes a change notification
const DWORD COXFileWatcher::OXFileWatchChangeDirName = FILE_NOTIFY_CHANGE_DIR_NAME;
// --- Any directory-name change in the watched directory or subtree causes a change notification
const DWORD COXFileWatcher::OXFileWatchChangeAttributes = FILE_NOTIFY_CHANGE_ATTRIBUTES;
// --- Any attribute change in the watched directory or subtree causes a change notification
const DWORD COXFileWatcher::OXFileWatchChangeSize = FILE_NOTIFY_CHANGE_SIZE;
// --- Any file-size change in the watched directory or subtree causes a change notification
// The operating system detects a change in file size only when the file is written to the disk.
// For operating systems that use extensive caching, detection occurs only when the cache is
// sufficiently flushed
const DWORD COXFileWatcher::OXFileWatchChangeLastWrite = FILE_NOTIFY_CHANGE_LAST_WRITE;
// --- Any change to the last write-time of files in the watched directory or subtree causes a
// change notification. The operating system detects a change to the last write-time only when
// the file is written to the disk. For operating systems that use extensive caching, detection
// occurs only when the cache is sufficiently flushed
const DWORD COXFileWatcher::OXFileWatchChangeLastAccess = FILE_NOTIFY_CHANGE_LAST_ACCESS;
// --- Any change to the last access time of files in the watched directory or subtree causes
// a change notification
const DWORD COXFileWatcher::OXFileWatchChangeCreation = FILE_NOTIFY_CHANGE_CREATION;
// --- Any change to the creation time of files in the watched directory or subtree causes
// a change notification
const DWORD COXFileWatcher::OXFileWatchChangeSecurity = FILE_NOTIFY_CHANGE_SECURITY;
// --- Any security-descriptor change in the watched directory or subtree causes a change notification
#if defined(_WIN32_WINNT)
BOOL COXFileWatcher::g_bBackupPrivilegeGranted=FALSE;
#endif
// protected:
// CArray<COXWatchedDir*, COXWatchedDir*> m_arWatchedDirs;
// --- Array with the watched directories
// COXIDManager<COXFileWatchNotifier> m_arFileWatchNotifiers;
// --- Array with the waiting notifier objects
// HRESULT m_hrError;
// --- the HRESULT value of the last error
// BOOL m_bThreadIsRunning;
// --- TRUE if the thread which is used to watch file change notifications is running
// CMutex m_mxThreadShouldStop;
// --- Is used to stop the watching thread
// CMutex m_mxThreadStopped;
// --- Is used to inform the main thread that the watcher thread is stopped
// and it is safe to make changes in m_arWatchedDirs
// CWinThread* m_pWatcherThread;
// --- A pointer to the watcher thread
// HANDLE* m_pHandles;
// --- Array with the watched handles
// HANDLE* m_pNewHandles;
// --- Array with the new handles after any changes in m_arWatchedDirs.
// WORD m_nNumHandles;
// --- the Number of watched handles
// private:
// Member functions ---------------------------------------------------------
// public:
COXFileWatcher::COXFileWatcher(BOOL bAddBackupPrivilege/*=TRUE*/) :
m_hrError(ERROR_SUCCESS),
m_bThreadIsRunning(FALSE),
m_mxThreadShouldStop(TRUE),
m_mxThreadStopped(FALSE),
m_mxThreadCanContinue(FALSE),
m_eventStartThread(FALSE,TRUE,NULL,NULL),
m_pHandles(NULL),
m_pNewHandles(NULL),
m_pWatcherThread(NULL),
m_nNumHandles(0),
m_bDeletingThread(FALSE)
{
#if defined(_WIN32_WINNT)
if(bAddBackupPrivilege)
{
if(!SetBackupPrivilege(TRUE))
{
AfxThrowNotSupportedException();
}
}
#else
UNREFERENCED_PARAMETER(bAddBackupPrivilege);
#endif
}
COXFileWatcher::~COXFileWatcher()
{
// Will stop thread if it's running and will delete handles
RemoveAllWatches();
// ... m_pWatcherThread may already be NULL
delete m_pWatcherThread;
}
#if defined(_WIN32_WINNT)
BOOL COXFileWatcher::SetBackupPrivilege(BOOL bAdd)
{
if(g_bBackupPrivilegeGranted==bAdd)
{
// already done
return TRUE;
}
TOKEN_PRIVILEGES NewState;
LUID luid;
HANDLE hToken=NULL;
BOOL bRtnStatus=TRUE;
// Open the process token for this process.
if(::OpenProcessToken(
::GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
{
// Get the local unique id for the privilege.
if(::LookupPrivilegeValue(NULL,_T("SeBackupPrivilege"),&luid))
{
// Assign values to the TOKEN_PRIVILEGE structure.
NewState.PrivilegeCount=1;
NewState.Privileges[0].Luid=luid;
NewState.Privileges[0].Attributes=(bAdd ? SE_PRIVILEGE_ENABLED : 0);
// Adjust the token privilege
if(!::AdjustTokenPrivileges(
hToken,FALSE,&NewState,sizeof(NewState),NULL,NULL))
{
bRtnStatus = FALSE;
}
}
else
{
bRtnStatus = FALSE;
}
}
else
{
bRtnStatus = FALSE;
}
if(hToken!=NULL)
{
::CloseHandle(hToken);
}
if(bRtnStatus)
{
g_bBackupPrivilegeGranted=bAdd;
}
return bRtnStatus;
}
#endif
BOOL COXFileWatcher::AddWatch(LPCTSTR pszPath, BOOL bWatchSubtree, DWORD dwWatchFilter)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT(pszPath != NULL);
// Clear error flag
m_hrError = ERROR_SUCCESS;
COXWatchedDir* pwdNewEntry;
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
// Fisrt check if we are not watching for the same path
for(int i=0; i<m_arWatchedDirs.GetSize(); i++)
{
if((m_arWatchedDirs[i])->m_sPath == pszPath)
{
// ... OK. The path is already here
COXWatchedDir* pwdPresentEntry = m_arWatchedDirs[i];
if(bWatchSubtree == pwdPresentEntry->m_bWatchSubTree &&
dwWatchFilter == pwdPresentEntry->m_dwWatchFilter)
{
// No modifications
return TRUE;
}
pwdNewEntry = new COXWatchedDir(
pszPath, // directory to be watched
bWatchSubtree|pwdPresentEntry->m_bWatchSubTree, // flag for monitoring directory or directory tree
dwWatchFilter|pwdPresentEntry->m_dwWatchFilter, // filter conditions to watch for
IsExtendedInfoSupported()); // flag for extended info
// Create new handle
if(!pwdNewEntry->FindFirstHandle(m_hrError))
{
goto error;
}
// Prepares the handles for the watcher thread
m_pNewHandles = m_pHandles;
VERIFY(m_mxThreadCanContinue.Lock());
// Informs the watcher thread that it can change the handles
// and waits for the watcher thread to stop
VERIFY(m_mxThreadShouldStop.Unlock());
// wait until the watcher thread stops
VERIFY(m_mxThreadStopped.Lock());
// Update the entry for the given path
m_arWatchedDirs.SetAt(i, pwdNewEntry);
// Close old entry. This will close the old handle
delete pwdPresentEntry;
// Replace the old handle with the new one and start the
// thread again
m_pHandles[i+1]=pwdNewEntry->m_hEvent;
VERIFY(m_mxThreadStopped.Unlock());
VERIFY(m_mxThreadShouldStop.Lock());
// notify watcher thread that it can go on executing
VERIFY(m_mxThreadCanContinue.Unlock());
return TRUE;
}
}
pwdNewEntry = new COXWatchedDir(pszPath, // directory to be watched
bWatchSubtree, // flag for monitoring directory or directory tree
dwWatchFilter, // filter conditions to watch for
IsExtendedInfoSupported());// flag for extended info
if(pwdNewEntry->FindFirstHandle(m_hrError))
{
// Handles are m_nNumHandles, so we must allocate (m_nNumHandles+2) : one for the StartStopEvent
// and one for the new entry
m_pNewHandles= new HANDLE[m_nNumHandles + 2];
// Informs the watcher thread that it can change the handles
if(m_bThreadIsRunning)
{
VERIFY(m_mxThreadCanContinue.Lock());
// notify watcher thread that it should stop
VERIFY(m_mxThreadShouldStop.Unlock());
// wait until it stops
DWORD dwResult=::WaitForSingleObject(m_mxThreadStopped.m_hObject, 0xFFFFFFFF);
ASSERT(dwResult==NULL);
//VERIFY(m_mxThreadStopped.Lock());
}
// update the array of directories
m_arWatchedDirs.Add(pwdNewEntry);
if(0 < m_nNumHandles)
{
memcpy(m_pNewHandles,m_pHandles,(m_nNumHandles+1)*sizeof(HANDLE));
}
else
{
m_pNewHandles[0]=HANDLE(m_mxThreadShouldStop);
}
m_pNewHandles[m_nNumHandles+1] = pwdNewEntry->m_hEvent;
// ... m_pHandles may be NULL
delete [] m_pHandles;
m_pHandles = m_pNewHandles;
m_nNumHandles++;
// Starts the watcher thread
if(m_bThreadIsRunning)
{
VERIFY(m_mxThreadStopped.Unlock());
VERIFY(m_mxThreadShouldStop.Lock());
// Unlocks the watcher thread
VERIFY(m_mxThreadCanContinue.Unlock());
return TRUE;
}
else
{
// ... m_pWatcherThread may already be NULL
delete m_pWatcherThread;
VERIFY(m_eventStartThread.ResetEvent());
m_pWatcherThread=AfxBeginThread(FileWatchThreadFunction,this);
if(m_pWatcherThread!=NULL)
{
m_pWatcherThread->m_bAutoDelete = FALSE;
::WaitForSingleObject(m_eventStartThread,INFINITE);
return TRUE;
}
}
}
error:
delete pwdNewEntry;
CONDITIONAL_TRACE_RESULT("COXFileWatcher::AddWatch", m_hrError)
return FALSE;
}
HRESULT COXFileWatcher::GetLastError() const
{
return m_hrError;
}
BOOL COXFileWatcher::IsExtendedInfoSupported() const
{
#if defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
// Extended info is supported
OSVERSIONINFO OSVersion;
OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
VERIFY(::GetVersionEx(&OSVersion));
// At the moment extended information is only supported on Windows NT 4.0
return (OSVersion.dwPlatformId==VER_PLATFORM_WIN32_NT &&
4<=OSVersion.dwMajorVersion);
#else
// Extended info is not supported
return FALSE;
#endif //defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
}
void COXFileWatcher::RemoveWatch(LPCTSTR pszPath)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT(pszPath != NULL);
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
// Fisrt find the given path in the local array
for(int i=0; i<m_arWatchedDirs.GetSize(); i++)
{
if((m_arWatchedDirs[i])->m_sPath==pszPath)
{
// If the handle is only one then stop the watcher thread
if(m_nNumHandles == 1)
m_pNewHandles = NULL;
else
m_pNewHandles=m_pHandles;
VERIFY(m_mxThreadCanContinue.Lock());
// notify watcher thread that it should stop
VERIFY(m_mxThreadShouldStop.Unlock());
// wait until it stops
// VERIFY(m_mxThreadStopped.Lock());
m_mxThreadStopped.Lock();
// Move all handles after the current one forward
for(int k=i+1; k<m_nNumHandles; k++)
{
m_pHandles[k]=m_pHandles[k+1];
}
m_nNumHandles--;
COXWatchedDir* pwdEntry = m_arWatchedDirs[i];
m_arWatchedDirs.RemoveAt(i);
delete (COXWatchedDir*)pwdEntry;
//critical section, before let the thread to go on
//set m_bDeletingThread to TRUE to inform the thread
//that we are deleting it. Otherwise, may came notification
//from windows exactly in the period time we have deleted
//the list watches, but thread had no time to finished,
//so, the thread receives notification while
//::WaitForSingleObject() in this function
if (m_pNewHandles==NULL)
m_bDeletingThread=TRUE;
VERIFY(m_mxThreadStopped.Unlock());
VERIFY(m_mxThreadShouldStop.Lock());
// notify watcher thread that it can go on executing
VERIFY(m_mxThreadCanContinue.Unlock());
if(m_pNewHandles!=NULL)
{
return;
}
// Wait for the watcher thread to stops
::WaitForSingleObject(m_pWatcherThread->m_hThread, INFINITE);
delete m_pWatcherThread;
m_pWatcherThread=NULL;
delete [] m_pHandles;
m_pHandles=NULL;
//end of the critical section, restore m_bDeletingThread
m_bDeletingThread=FALSE;
return;
}
}
}
void COXFileWatcher::RemoveAllWatches()
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
StopThread();
if(m_arWatchedDirs.GetSize() == 0)
return;
for(int i=0; i<=m_arWatchedDirs.GetUpperBound(); i++)
{
delete m_arWatchedDirs[i];
}
m_arWatchedDirs.RemoveAll();
if(m_pHandles!=NULL)
{
delete [] m_pHandles;
m_pHandles = NULL;
}
m_nNumHandles = 0;
}
BOOL COXFileWatcher::OnNotify(COXFileWatchNotifier fileWatchNotifier)
{
return FALSE;
}
void COXFileWatcher::EnableWindowNotification(LPCTSTR pszPath, CWnd* pWnd, BOOL bPost)
{
ASSERT(pszPath!=NULL);
ASSERT(pWnd!=NULL && pWnd->m_hWnd!=NULL);
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
// ... Looking for the path
for(int i=0; i<=m_arWatchedDirs.GetUpperBound(); i++)
{
if((m_arWatchedDirs[i])->m_sPath == pszPath)
{
COXWatchedDir* pwdEntry = m_arWatchedDirs[i];
pwdEntry->m_hwndWindowToNotify = pWnd->m_hWnd;
pwdEntry->m_bPost = bPost;
break;
}
}
}
COXFileWatchNotifier COXFileWatcher::GetFileWatchNotifier(DWORD fileWatchNotifierID)
{
COXFileWatchNotifier* pfwnRequestedNotifier=m_arFileWatchNotifiers.GetItemPtr(fileWatchNotifierID);
COXFileWatchNotifier aFileWatchNotifier;
if(pfwnRequestedNotifier != NULL)
{
aFileWatchNotifier = *pfwnRequestedNotifier;
// Remove the notifier object, so it is no more accessable
m_arFileWatchNotifiers.RemoveItem(fileWatchNotifierID);
delete pfwnRequestedNotifier;
}
return aFileWatchNotifier;
}
void COXFileWatcher::DisableWindowNotification(LPCTSTR pszPath)
{
ASSERT(pszPath!=NULL);
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
if(pszPath == NULL)
return;
// ... Looking for the path again
for(int i=0; i<=m_arWatchedDirs.GetUpperBound(); i++)
{
if((m_arWatchedDirs[i])->m_sPath == pszPath)
{
COXWatchedDir* pwdEntry = m_arWatchedDirs[i];
pwdEntry->m_hwndWindowToNotify = NULL;
break;
}
}
}
void COXFileWatcher::Empty()
{
RemoveAllWatches();
m_arFileWatchNotifiers.DeleteAll();
}
#ifdef _DEBUG
void COXFileWatcher::AssertValid()
{
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
CObject::AssertValid();
for(int i=0; i<m_arWatchedDirs.GetSize(); i++)
{
COXWatchedDir* pwd = m_arWatchedDirs[i];
pwd->AssertValid();
}
}
void COXFileWatcher::Dump(CDumpContext& dc)
{
// Make the class a Thread-Safe Class
CSingleLock lockObj(&m_crArrayWatchedDirGuard);
VERIFY(lockObj.Lock());
CObject::Dump(dc);
dc << _T("\nNumber of watched paths: ");
dc << m_arWatchedDirs.GetSize() << _T("\n");
if (dc.GetDepth() > 0)
{
for(int i=0; i<m_arWatchedDirs.GetSize(); i++)
{
COXWatchedDir* pwd = m_arWatchedDirs[i];
pwd->Dump(dc);
}
}
}
static TCHAR szUnknownError[] = _T("*** Unknown Error ***");
static DWORD dwLangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
CString COXFileWatcher::GetResultMessage(HRESULT hResult)
// --- In : hResult : The result code
// --- Out :
// --- Returns : A string containing a message of the specified code
// --- Effect : Retrieves the error message corresponding to the given hResult
{
CString sResultMessage;
LPTSTR pszMsgBuf = NULL;
BOOL bUnknown = FALSE;
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
// ... Remove the facility part if FACILITY_WIN32
if (HRESULT_FACILITY(hResult) == FACILITY_WIN32)
hResult = HRESULT_CODE(hResult);
// ... Get the actual message
if (::FormatMessage(dwFlags, NULL, hResult, dwLangID,
(LPTSTR)&pszMsgBuf, 0, NULL) == 0)
{
TRACE2("COXFileWatcher::GetResultMessage : No message was found for result code %i == 0x%8.8X\n",
hResult, hResult);
//pszMsgBuf = szUnknownError;
VERIFY(sResultMessage.LoadString(IDS_OX_FILEWATCHERUNKERROR));
bUnknown = TRUE;
}
else
sResultMessage = pszMsgBuf;
// ... Clean up
if (!bUnknown)
LocalFree(pszMsgBuf);
return sResultMessage;
}
#endif // _DEBUG
// protected:
UINT COXFileWatcher::FileWatchThreadFunction(LPVOID pParam)
// --- In : pParam: a pointer to the current COXFileWatcher object
// --- Out :
// --- Returns :
// --- Effect : Used as a worker thread function
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT(pParam!=NULL);
COXFileWatcher* pThis=(COXFileWatcher*)pParam;
HANDLE* pHandles=pThis->m_pHandles;
pThis->m_bThreadIsRunning = TRUE;
VERIFY(pThis->m_eventStartThread.SetEvent());
#if defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
// Extended info is supported
static TCHAR szFileName[_MAX_PATH];
// --- the name of the file if extended info is available
static TCHAR szNewFileName[_MAX_PATH];
// --- the name of the new file if extended info is available and action is rename
FILE_NOTIFY_INFORMATION* pfnInfo=NULL;
// -- pointer to a buffer with extended information (if available)
#endif // defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
COXFileWatchNotifier* pfwnNotifier;
// --- the notifier object that will be posted/sended
DWORD nCount=PtrToLong(pThis->m_arWatchedDirs.GetSize()+1);
// Exit from the loop only when the StartStopEvent as signaled
while(TRUE)
{
DWORD dwRetValue = WaitForMultipleObjects(nCount, pHandles,
FALSE, INFINITE);
// Check for error
if(dwRetValue == WAIT_FAILED || dwRetValue<WAIT_OBJECT_0 ||
dwRetValue > (WAIT_OBJECT_0 + nCount - 1))
{
pThis->m_hrError = HRESULT_FROM_WIN32(::GetLastError());
break;
}
else
{
// To stop the thread or a notification is signaled
if(dwRetValue == WAIT_OBJECT_0)
{
VERIFY(pThis->m_mxThreadShouldStop.Unlock());
// Informs the main thread that we are waiting
// VERIFY(pThis->m_mxThreadStopped.Unlock());
pThis->m_mxThreadStopped.Unlock();
// Wait for the main thread
VERIFY(pThis->m_mxThreadCanContinue.Lock());
VERIFY(pThis->m_mxThreadStopped.Lock());
VERIFY(pThis->m_mxThreadCanContinue.Unlock());
// In this point the main thread should prepare the handles
if(pThis->m_pNewHandles==NULL)
{
break; // Stops the thread
}
// Set new handles
pHandles=pThis->m_pNewHandles;
nCount = PtrToLong(pThis->m_arWatchedDirs.GetSize()+1);
continue;
}
else
{
// ... OK. New notification
//if notification comes in the period of the time
//that this thread is to be deleted, so do not
//process the notification.
if (pThis->m_bDeletingThread)
break;
// Find the directory entry
int nSignaledPath=dwRetValue-WAIT_OBJECT_0-1;
COXWatchedDir* pwdPath = (pThis->m_arWatchedDirs)[nSignaledPath];
#if defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
// Extended info is supported
DWORD dwNumberOfBytesTransferred;
BOOL bValid;
if(pwdPath->m_bExtended)
{
// ... OK. We have extended info. Take a pointer to it.
pfnInfo=(FILE_NOTIFY_INFORMATION*)pwdPath->m_lpBuffer;
// ... we have to check if the notification is valid or not
// The check must be done before FindNextHandle()
bValid= ::GetOverlappedResult(pwdPath->m_hDirectory, // handle of the directory
pwdPath->m_pOverlapped, // address of overlapped structure
&dwNumberOfBytesTransferred, // address of actual bytes count
FALSE); /// wait flag
if(bValid == FALSE || dwNumberOfBytesTransferred == 0)
{
// Something is wrong
#ifdef _DEBUG
if(bValid == FALSE)
{
pThis->m_hrError = HRESULT_FROM_WIN32(::GetLastError());
CONDITIONAL_TRACE_RESULT("COXFileWatcher::FileWatchThreadFunction", pThis->m_hrError)
}
else // dwNumberOfBytesTransferred == 0
{
TRACE(_T("\nThe buffer passed to ReadDirectoryChangesW() is too small!\n"));
}
#endif // _DEBUG
// ... update the handle
if(!pwdPath->FindNextHandle((pThis->m_pHandles)[nSignaledPath+1]))
{
pThis->m_hrError=
HRESULT_FROM_WIN32(::GetLastError());
break;
}
// The extended information is available, but it is not valid
continue;
}
}
while(TRUE)
{
if(pwdPath->m_bExtended)
{
// Extended information is available
memcpy(szFileName,(LPCTSTR)&pfnInfo->FileName[0],
pfnInfo->FileNameLength);
szFileName[pfnInfo->
FileNameLength/sizeof(TCHAR)]=_T('\0');
if(pfnInfo->Action == FILE_ACTION_RENAMED_OLD_NAME)
{
pfnInfo=(FILE_NOTIFY_INFORMATION*)(((char*)pfnInfo)+
pfnInfo->NextEntryOffset);
memcpy(szNewFileName, (LPCTSTR)&pfnInfo->FileName[0],
pfnInfo->FileNameLength);
szNewFileName[pfnInfo->
FileNameLength/sizeof(TCHAR)]=_T('\0');
}
else
{
szNewFileName[0]=_T('\0');
}
pfwnNotifier= new COXFileWatchNotifier(pwdPath->m_sPath,
pwdPath->m_bWatchSubTree,pwdPath->m_dwWatchFilter,
TRUE,pfnInfo->Action,szFileName,szNewFileName);
}
else
#endif // defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
{
// ... there is no extended information
pfwnNotifier= new COXFileWatchNotifier(pwdPath->m_sPath,
pwdPath->m_bWatchSubTree,pwdPath->m_dwWatchFilter,
FALSE,0,NULL,NULL);
}
// Notify the derived class or ...
if(!pThis->OnNotify(*pfwnNotifier) &&
pwdPath->m_hwndWindowToNotify!=NULL)
{
// ... post/send message
UINT nItemIndex=pThis->m_arFileWatchNotifiers.
AddItem(pfwnNotifier);
if(pwdPath->m_bPost)
{
::PostMessage(pwdPath->m_hwndWindowToNotify,
WM_OX_FILE_NOTIFY,nItemIndex,LPARAM(0));
}
else
{
::SendMessage(pwdPath->m_hwndWindowToNotify,
WM_OX_FILE_NOTIFY,nItemIndex,LPARAM(0));
}
}
else
{
// the notifier object is not queued so delete it
delete pfwnNotifier;
}
#if defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
// Extended info is supported
if(!pwdPath->m_bExtended || pfnInfo->NextEntryOffset == 0)
{
break;
}
else
{
// ... go to the next entry in the buffer...
pfnInfo = (FILE_NOTIFY_INFORMATION*)(((char*)pfnInfo)+
pfnInfo->NextEntryOffset);
}
} // while(TRUE);
#endif // defined(_UNICODE) && (_WIN32_WINNT >= 0x400)
// ... update the handle
if(!pwdPath->FindNextHandle((pThis->m_pHandles)[nSignaledPath+1]))
{
pThis->m_hrError = HRESULT_FROM_WIN32(::GetLastError());
break;
}
}
}
}
// The thread is stopping
pThis->m_bThreadIsRunning = FALSE;
return 0;
}
void COXFileWatcher::StopThread()
// --- In :
// --- Out :
// --- Returns :
// --- Effect : Stops the thread
{
if(!m_bThreadIsRunning)
return;
VERIFY(m_mxThreadCanContinue.Lock());
// Informs the watcher thread that it can change the handles
// and waits for the watcher thread to stop
VERIFY(m_mxThreadShouldStop.Unlock());
// wait until the watcher thread stops
// VERIFY(m_mxThreadStopped.Lock());
m_mxThreadStopped.Lock();
// The watcher thread will stop if there are no more handles to watch
m_pNewHandles = NULL;
VERIFY(m_mxThreadStopped.Unlock());
VERIFY(m_mxThreadShouldStop.Lock());
// notify watcher thread that it can go on executing
VERIFY(m_mxThreadCanContinue.Unlock());
// Wait for the watcher thread to stops
::WaitForSingleObject(m_pWatcherThread->m_hThread, INFINITE);
delete m_pWatcherThread;
m_pWatcherThread = NULL;
}
// private: