Click here to Skip to main content
15,896,405 members
Articles / Desktop Programming / MFC

CDirectoryChangeWatcher - ReadDirectoryChangesW all wrapped up

Rate me:
Please Sign up or sign in to vote.
4.92/5 (114 votes)
11 May 2002 2.2M   39.4K   365  
This class wraps up ReadDirectoryChangesW.
// DirectoryChanges.h: interface for the 
// CDirectoryChangeWatcher and CDirectoryChangeHandler classes.
//
//  Uses an io completion port and ReadDirectoryChangesW -- this code will only work on 
//	Windows NT/2000.
//
//	The directory being watched must be a directory on a Windows NT/2000 machine
//
//
//	These classes are based on the FWatch sample program in the sdk.
//
//
//	If you get a compile time error that ReadDirectoryChangesW is an undeclared identifier,
//  you'll need to #define _WIN32_WINNT 0x400 in stdafx.h.
//
//
//	Copyright 2001. Wes Jones (wesj@hotmail.com)
//
//	This code is free, unless you want to give me money.
//  You are free to do whatever you want with the souce code.
//
//  No guarantees or warranties are expressed or implied.
//  Please let me know of any bugs, bug fixes, or enhancements made to this code.
//  If you have better ideas for this, and/or tips or admonitions, I would be glad to hear them.
//
//
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_)
#define AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <afxmt.h>
#include <afxtempl.h>

#define READ_DIR_CHANGE_BUFFER_SIZE 4096

class CDirectoryChangeWatcher;

class CDirectoryChangeHandler
/***********************************
 A class to handle changes to files in a directory.  The virtual On_Filexxx() functions
 are called by a worker thread whenever changes are made to a watched directory that is being handled by this object...

  NOTES: 
		A CDirectoryChangeHandler can only be used by ONE CDirectoryChangeWatcher object,
		but a CDirectoryChangeWatcher object may use multiple CDirectoryChangeHandler objects.

	When this object is destroyed, whatever directories that it was being used to handle directory changes for
	will automatically be 'unwatched'.

	The class is reference counted.  The reference count is increased every time it is used 
	in a (successfull) call to CDirectoryChangeWatcher::WatchDirectory() and is decremented whenever
	the directory becomes 'unwatched'.

   The only notifications are File Added, Removed, Modified, and renamed.
   Even though the CDirectoryChangeWatcher::WatchDirectory (which'll call ReadDirectoryChangesW()) function allows you to specify flags 
   to watch for changes to last access time, last write time, attributes changed, etc,
   these changes all map out to On_FileModified() which doesn't specify the type of modification

************************************/
{
public:

	CDirectoryChangeHandler();
	virtual ~CDirectoryChangeHandler();

	//this class is reference counted
	long AddRef(){ return InterlockedIncrement(&m_nRefCnt);	}
	long Release()
		{  long nRefCnt = -1;
			if( (nRefCnt = InterlockedDecrement(&m_nRefCnt)) == 0 )
				delete this;
			return nRefCnt;
		}
	long CurRefCnt()const { return m_nRefCnt;}


	BOOL UnwatchDirectory();
	
	CString GetChangedDirectoryName() const { return m_strChangedDirectoryName;}
								//returns the directory name where the change occured.  This contains
							   //the last directory to have changed if the same CDirectoryChangeHandler is
							   //being used to watch multiple directories. It will return an empty string
							   //if no changes have been made to a directory yet.   It will always be the 
							   //name of the currently changed directory(as specified in CDirectoryChangeWatcher::WatchDirectory())
							   //if called in the context of one of the
							   //On_Filexxx() functions.
protected:
	//These functions are called when the directory to watch has had a change made to it
	virtual void On_FileAdded(const CString & strFileName); //=0;
	virtual void On_FileRemoved(const CString & strFileName);// = 0;
	virtual void On_FileModified(const CString & strFileName);// = 0;
	virtual void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName);// = 0;

	virtual void On_ReadDirectoryChangesError(DWORD dwError){
		TRACE(_T("ReadDirectoryChangesW has failed! %d"), dwError);
	}
private:
	long m_nRefCnt;
	CString m_strChangedDirectoryName;

	friend class CDirectoryChangeWatcher;
	//This class keeps a reference to the CDirectoryChangeWatcher 
	//that was used when an object of this type is passed 
	//to CDirectoryChangeWatcher::WatchDirectory()
	//This way, when the CDirecotryChangeHandler object is destroyed(or if CDirectoryChangeHandler::UnwatchDirectory() is called)
	//AFTER CDirectoryChangeWatcher::UnwatchDirecotry() or CDirecotryChangeWatcher::UnwatchAllDirectories() is called 
	//the directory(or direcotries) that this 
	//CDirecotryChangeHandler object is handling will be automatically unwatched
	//If the CDirecotryChangeWatcher object is destroyed before the CDirectoryChangeHandler objects 
	//that are being used with that watcher are destroyed, the reference counting prevents
	//this class from referencing a destructed object.
	//Basically, neither class needs to worry about the lifetime of the other(CDirectoryChangeWatcher && CDirectoryChangeHandler)

	long  ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher);
	long  ReleaseReferenceToWatcher(CDirectoryChangeWatcher * pDirChangeWatcher);
	CDirectoryChangeWatcher * m_pDirChangeWatcher;
	long   m_nWatcherRefCnt; //<-- when this reaches 0, m_pDirChangeWatcher is set to NULL
	CCriticalSection m_csWatcher;
};

class CDirectoryChangeWatcher  
/***************************************
  A class to monitor a directory for changes made to it.
  The class CDirectoryChangeHandler handles the changes.


  Multiple directories can be watched simultaneously using a single instance of CDirectoryChangeWatcher.
  Single or multiple instances of CDirectoryChangeHandler object(s) can be used to handle changes to watched directories.
  Directories can be added and removed from the watch dynamically at run time without destroying 
  the CDirectoryChangeWatcher object (or CDirectoryChangeHandler object(s).

  This class uses a single worker thread, an io completion port, and ReadDirectoryChangesW() to monitor changes to a direcory (or subdirectories).
  There will always only be a single thread no matter how many directories are being watched(per instance of CDirectoryChangeHandler)
  
  
****************************************/
{
public:
	CDirectoryChangeWatcher();
	virtual ~CDirectoryChangeWatcher();

	DWORD WatchDirectory(const CString & strDirToWatch, DWORD dwChangesToWatchFor, CDirectoryChangeHandler * pChangeHandler, BOOL bWatchSubDirs = FALSE);

	BOOL IsWatchingDirectory (const CString & strDirName);
	
	BOOL UnwatchDirectory(const CString & strDirToStopWatching);
	BOOL UnwatchAllDirectories();

protected:
	
	virtual void On_ThreadInitialize(){}//All file change notifications has taken place in the context of a worker thread...do any thread initialization here..
	virtual void On_ThreadExit(){}//do thread cleanup here
	
private:
	friend class CDirectoryChangeHandler;
	BOOL UnwatchDirectory(CDirectoryChangeHandler * pChangeHandler);//called in CDirectoryChangeHandler::~CDirectoryChangeHandler()


	UINT static MonitorDirectoryChanges(LPVOID lpvThis );
	
	class CDirWatchInfo 
		//this class is used internally by CDirecotryChangeWatcher
		//to help manage the watched directories
	{
	public:
		CDirWatchInfo(HANDLE hDir, const CString & strDirectoryName, 
					  CDirectoryChangeHandler * pChangeHandler, 
					  DWORD dwChangeFilter, BOOL bWatchSubDir)
			: m_pChangeHandler( pChangeHandler ),
			  m_hDir(hDir),
			  m_dwChangeFilter( dwChangeFilter ),
			  m_bWatchSubDir( bWatchSubDir ),
			  m_strDirName( strDirectoryName ),
			  m_dwBufLength(0),
			  m_dwReadDirError(ERROR_SUCCESS),
			  m_StartStopEvent(FALSE, TRUE), //NOT SIGNALLED, MANUAL RESET
			  m_RunningState( RUNNING_STATE_NOT_SET )
 		{ 
			ASSERT( hDir != INVALID_HANDLE_VALUE 
				&& !strDirectoryName.IsEmpty() );

			ASSERT( m_pChangeHandler );
			if( m_pChangeHandler )
				m_pChangeHandler->AddRef();
			
			memset(&m_Overlapped, 0, sizeof(m_Overlapped));
		}
		~CDirWatchInfo( )
		{
			if( m_pChangeHandler )
			{//If this call to CDirectoryChangeHandler::Release() causes m_pChangeHandler to delete itself,
			 //the dtor for CDirectoryChangeHandler will call CDirectoryChangeWatcher::UnwatchDirectory( CDirectoryChangeHandler * ),
			 //which will make try to delete this object again.
			  //if m_pChangeHandler is NULL, it won't try to delete this object again...
				CDirectoryChangeHandler * pTmp = m_pChangeHandler;
				m_pChangeHandler = NULL;
				pTmp->Release();
			}
			
			
			if( m_hDir != INVALID_HANDLE_VALUE ) 
			{ 
				CloseHandle( m_hDir ), m_hDir = INVALID_HANDLE_VALUE;
			}
		
		}
		DWORD StartMonitor(HANDLE hCompPort);
		BOOL UnwatchDirectory( HANDLE hCompPort);
		BOOL LockProperties() { return m_cs.Lock(); }
		BOOL UnlockProperties(){ return m_cs.Unlock();	}
		
		CDirectoryChangeHandler * m_pChangeHandler;
		HANDLE      m_hDir;//handle to directory that we're watching
		DWORD		m_dwChangeFilter;
		BOOL		m_bWatchSubDir;
		CString     m_strDirName;//name of the directory that we're watching
		CHAR        m_Buffer[ READ_DIR_CHANGE_BUFFER_SIZE ];//buffer for ReadDirectoryChangesW
		DWORD       m_dwBufLength;//length or returned data from ReadDirectoryChangesW -- ignored?...
		OVERLAPPED  m_Overlapped;
		DWORD		m_dwReadDirError;//indicates the success of the call to ReadDirectoryChanges()
		CCriticalSection m_cs;
		CEvent		m_StartStopEvent;
		enum eRunningState{
			 RUNNING_STATE_NOT_SET, DO_START_MONITOR, DO_STOP_MONITOR, DO_STOP_MONITOR_STEP2,
			 DO_RUN_NORMAL,  };
		eRunningState m_RunningState;
		};

	HANDLE m_hCompPort;
	HANDLE m_hThread;
	//DWORD  m_dwThreadID;
	CTypedPtrArray<CPtrArray, CDirWatchInfo*> m_DirectoriesToWatch;
	CCriticalSection m_csDirWatchInfo;
	
};

#endif // !defined(AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_)

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions