Click here to Skip to main content
13,141,354 members (91,199 online)
Click here to Skip to main content


353 bookmarked
Posted 31 Jan 2001

CDirectoryChangeWatcher - ReadDirectoryChangesW all wrapped up

, 11 May 2002
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 (
//	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_)

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

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


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

		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


	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.
	//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);
	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)
	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();

	virtual void On_ThreadInitialize(){}//All file change notifications has taken place in the context of a worker any thread initialization here..
	virtual void On_ThreadExit(){}//do thread cleanup here
	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
		CDirWatchInfo(HANDLE hDir, const CString & strDirectoryName, 
					  CDirectoryChangeHandler * pChangeHandler, 
					  DWORD dwChangeFilter, BOOL bWatchSubDir)
			: m_pChangeHandler( pChangeHandler ),
			  m_dwChangeFilter( dwChangeFilter ),
			  m_bWatchSubDir( bWatchSubDir ),
			  m_strDirName( strDirectoryName ),
			  m_RunningState( RUNNING_STATE_NOT_SET )
				&& !strDirectoryName.IsEmpty() );

			ASSERT( m_pChangeHandler );
			if( m_pChangeHandler )
			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;
			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{
			 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.


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


About the Author

Wes Jones
Web Developer
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170915.1 | Last Updated 12 May 2002
Article Copyright 2001 by Wes Jones
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid