Click here to Skip to main content
15,891,423 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 255.5K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ==========================================================================
//				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:

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions