CDirectoryChangeWatcher - ReadDirectoryChangesW all wrapped up

DirWatcher Sample App Screenshot

Introduction

This code wraps up the Win32 API function ReadDirectoryChangesW            so that your application only has to worry about responding to the   events   that take place when a file or directory is added, removed, modified,   or   renamed.

This code will only work on Windows NT, Windows 2000, or Windows XP, and the directory     you wish to watch must also reside on a WindowsNT, Windows 2000, or Windows XP computer.

The Classes

There are two classes that must be used together to watch a directory, they   are: CDirectoryChangeWatcher and CDirectoryChangeHandler.

About CDirectoryChangeWatcher:

The class CDirectoryChangeWatcher            does all the grunt work of watching a directory.  It creates   a  worker   thread that is waiting for directory changes to take place via  its  use of  the            ReadDirectoryChangesW            Win32 API function.   Multiple directories can be watched    with   a single instance of CDirectoryChangeWatcher, and directories     can be added and removed from the watch at any time.  

When file change notifications are received, CDirectoryChangeWatcher            'dispatches' these notifications to your application via the the   class    CDirectoryChangeHandler(see below).

About CDirectoryChangeHandler:

The class CDirectoryChangeHandler receives notifications     about file changes from CDirectoryChangeHandler.  You   need  to derive a class from CDirectoryChangeHandler in order    for  your application to handle the file change notifications. 
          A single instance of a CDirectoryChangeHandler  derived    class can be used to handle notifications for multiple directory watches   at the same time, or you may specify different CDirectoryChangeHandler           derived classes for each watched directory.

The Interfaces

The following is the public interface     of CDirectoryChangeWatcher:

class CDirectoryChangeWatcher{
public: 
   enum	{  //options for determining the behavior of the filter tests.
           FILTERS_DONT_USE_FILTERS     = 1, 
	   FILTERS_CHECK_FULL_PATH	= 2, 
	   FILTERS_CHECK_PARTIAL_PATH	= 4, 
	   FILTERS_CHECK_FILE_NAME_ONLY	= 8, 
	   FILTERS_TEST_HANDLER_FIRST	= 16,
	   FILTERS_DONT_USE_HANDLER_FILTER = 32, 
	   FILTERS_DEFAULT_BEHAVIOR	= (FILTERS_CHECK_FILE_NAME_ONLY ),
	   FILTERS_DONT_USE_ANY_FILTER_TESTS = (FILTERS_DONT_USE_FILTERS | 
                                                 FILTERS_DONT_USE_HANDLER_FILTER)
	};
  CDirectoryChangeWatcher(bool bAppHasGUI = true, 
                          DWORD dwFilterFlags = FILTERS_DEFAULT_BEHAVIOR);
  virtual ~CDirectoryChangeWatcher();//dtor
 
  DWORD	WatchDirectory( const CString & strDirToWatch, 
			DWORD dwChangesToWatchFor, 
			CDirectoryChangeHandler * pChangeHandler, 
			BOOL bWatchSubDirs = FALSE,
			LPCTSTR szIncludeFilter = NULL,
			LPCTSTR szExcludeFilter = NULL);

  BOOL IsWatchingDirectory(const CString & strDirName)const;
  int  NumWatchedDirectories()const; 

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

  DWORD	SetFilterFlags(DWORD dwFilterFlags);
  DWORD GetFilterFlags() const;

...
};


           The class CDirectoryChangeHandler has the following  interface:

class CDirectoryChangeHandler{
   ...
   BOOL UnwatchDirectory(); 
   ...
 protected:
  //override these functions:	
   //Notification related:
   virtual void On_FileAdded(const CString & strFileName);
   virtual void On_FileRemoved(const CString & strFileName);
   virtual void On_FileModified(const CString & strFileName);
   virtual void On_FileNameChanged(const CString & strOldFileName, 
                                   const CString & strNewFileName);
   virtual void On_ReadDirectoryChangesError(DWORD dwError);
   virtual void On_WatchStarted(DWORD dwError, const CString & strDirectoryName);
   virtual void On_WatchStopped(const CString & strDirectoryName);
   //Filter related:
   virtual bool On_FilterNotification(DWORD dwNotifyAction,
                                      LPCTSTR szFileName, LPCTSTR szNewFileName);
   ...
  };

To handle the events that happen when a file or directory is added, deleted,      modified, or renamed, create a class derived from CDirectoryChangeHandler            that does all of the things that you want to do when these events   happen.

The Notifications

There are 7 notifications that you can receive    when using these classes.

 

Notification
CDirectoryChangeHandler       functionNotification Description
Flag(s) required to receive notification--      
(dwChangesToWatchFor parameter
       to
CDirectoryChangeWatcher::<br>
      WatchDirectory()
)
Watch StartedOn_WatchStarted
A directory watch has been started because        CDirectoryChangeWatcher::WatchDirectory was called.N/A
Watch StoppedOn_WatchStopped
A directory watch has been stopped because
 
      CDirectoryChangeWatcher::<br>
      UnwatchDirectory
or
<br>
      CDirectoryChangeWatcher::<br>
      UnwatchAllDirectories
       was called.

      Can also be called when CDirectoryChangeHandler's desctructor   is called and there are 1 or more directories being watched at that time.        
      ** See the comments in DirectoryChanges.h near the On_WatchStopped function   to avoid RTFM errors which can occur under certain circumstances.**
N/A
Directory Watch ErrorOn_ReadDirectoryChangesError
An error occurred while watching a directory.       In this condition, the watch is stopped automatically.  You will not       receive the On_WatchStopped notification.N/A
File AddedOn_FileAdded
A file was added to a watched directory (newly       created, or copied into that directory).FILE_NOTIFY_CHANGE_FILE_NAME and/or FILE_NOTIFY_CHANGE_DIR_NAME       (for directories)
File RemovedOn_FileRemoved
A file was deleted, or removed from the watched       directory(ie: sent to the recycle bin, moved to another directory, or deleted)FILE_NOTIFY_CHANGE_FILE_NAME  FILE_NOTIFY_CHANGE_DIR_NAME
File Name ChangedOn_FileNameChanged
A file's name has been changed in the watched       directory.  The parameters to this notification are the old file name,       and the new file name.FILE_NOTIFY_CHANGE_FILE_NAME  FILE_NOTIFY_CHANGE_DIR_NAME
File ModifiedOn_FileModified
A file was modified in some manner in a watched       directory.  Things that can cause you to receive this notification       include changes to a file's last accessed, last modified, or created timestamps.       Other changes, such as a change to a file's attributes, size, or security       descriptor can also trigger this notification.FILE_NOTIFY_CHANGE_ATTRIBUTES
       FILE_NOTIFY_CHANGE_SIZE
       FILE_NOTIFY_CHANGE_LAST_WRITE
       FILE_NOTIFY_CHANGE_LAST_ACCESS
       FILE_NOTIFY_CHANGE_CREATION
       FILE_NOTIFY_CHANGE_SECURITY

The Filters

One of the new features to this class is the ability to   filter out notifications so that you can receive notifications only for files   that you want to receive notifications about.  This feature enables you   receive file notifications only for files you wish to know about based on the   name of the changed file, or based on a function that you implement.  

  Without a filter, you will receive notifications for any and all file changes,   as specified by the combination of flags passed as the dwChangesToWatchFor   parameter to the CDirectoryChangeWatcher::WatchDirectory function(which   is the default by the way).

There are 3 types of filters: An Include Filter, an Exclude Filter, and a programmer implemented virtual   function which can decide whether or not the appropriate CDirectoryChangeHandler::On_Filexxxxx()   function is called.

The Include, Exclude filters:

The Include and Exclude filters are string parameters that   are passed to CDirectoryChangeWatcher::WatchDirectory when   a directory watch is started.  The filter is a pattern which can contain   the DOS wildcard characters * and ?.   Multiple patterns can be specified   by separating each pattern with a semi-colon (;).

  The following are examples of valid pattern strings that can be used to filter   notifications:

    "*.txt"   <-- specifies only a single file pattern.
      "*.txt;*.tmp;myfile???.txt;MySubFolder???\*.doc"  <--   specifies multiple file patterns.

Note that the supporting code for these string filters uses the    PathMatchSpec Win32 API function to determine a pattern match.    PathMatchSpec is implemented in shlwapi.dll version 4.71 or later. If you   are running on NT4.0, and Internet Explorer 4.0 is not installed, a modified   version of wildcmp is used instead.

What does the Include Filter mean?

The Include filter is used to tell CDirectoryChangeWatcher       that you wish to receive notifications ONLY for files that match a  certain   pattern. All other file notifications are implicitly excluded because  the   file name does not match the 'Include Filter' pattern. ie: If you pass an 'Include Filter' of "*.txt", you will  only  receive notifications(File Added, File Removed, etc) for files with  names  that end with ".txt".  You will not be notified of changes to  any other  files.

Note: It's better to specify a NULL or empty string, than to specify an   Include Filter of "*.*".

What does the Exclude Filter mean?

With the Exclude Filter, you can choose to tell CDirectoryChangeWatcher       to explicitly ignore notifications for changes to files based on the  name  of the changed file.  For example, you can choose to ignore changes  to .log files in a watched directory. To do so, you would specify an Exclude   Filter of "*.log"

The Include and Exclude Filters are a powerful way of customizing the notifications that you wish to receive.  To test your pattern strings, there is a dialog provided as part of the sample application.  Press the "Test Filter Patterns..." button on the Sample App.

The Programmer Defined Filter:

You can also override the function : virtual bool CDirectoryChangeHandler::On_FilterNotification()      . If On_FilterNotification returns true(the default), you   will  receive the notification.  If On_FilterNotification     returns  false, the file notification is ignored.  See the comments  in the source  code for more information about this function.

Filter Options

There are several options related to how CDirectoryChangeWatcher       works with filters. 

 

Filter Flag
Flag Description
FILTERS_DONT_USE_FILTERSSpecifies that the string filters for the Include and Exclude       filter are not checked.  All notifications are sent unless        CDirectoryChangeHandler::On_FilterNotification() returns false.
FILTERS_DONT_USE_HANDLER_FILTERSpecifies that CDirectoryChangeHandler::On_FilterNotification()       is not tested. If the File name passes the test against the Include and       Exclude filter, the notification is passed on.
FILTERS_DONT_USE_ANY_FILTER_TESTSSpecifies that NO filter tests are to be performed.  The       Include and Exclude filters are not tested, and CDirectoryChangeHandler::On_FilterNotification()       is not called.  This is a combination of FILTERS_DONT_USE_FILTERS and       FILTERS_DONT_USE_HANDLER_FILTER flags.
FILTERS_NO_WATCHSTART_NOTIFICATIONCDirectoryChangeHandler::On_WatchStarted won't       be called
FILTERS_NO_WATCHSTOP_NOTIFICATIONCDirectoryChangeHandler::On_WatchStopped won't       be called
FILTERS_NO_STARTSTOP_NOTIFICATIONCDirectoryChangeHandler::On_WatchStarted and       CDirectoryChangeHandler::On_WatchStopped won't be called. Is       a combination of FILTERS_NO_WATCHSTART_NOTIFICATION and FILTERS_NO_WATCHSTOP_NOTIFICATION.
FILTERS_TEST_HANDLER_FIRSTSpecifies that CDirectoryChangeHandler::On_FilterNotification()       is to be called BEFORE the file name is tested against the Include and Exclude       filter patterns.  The default is that CDirectoryChangeHandler::On_FilterNotification()       is tested AFTER only if the file name matches the patterns used that may       have been specified as Include or Exclude Filters.
FILTERS_CHECK_FULL_PATHSpecifies that the entire file name and path is to be tested       against the Include and Exclude Filter pattern.
FILTERS_CHECK_PARTIAL_PATHSpecifies that only a portion of the file path and name are       to be tested against the Include and Exclude Filter.  The portion of       the path checked is the part of the path following the watched folder name.       

      For example, if you are watching "C:\Temp" (and are also watching subfolders)       and a file named "C:\Temp\SomeFolder\SomeFileName.txt" is changed, the portion       of the file name that is checked against the Include and Exclude filters       is "SomeFolder\SomeFileName.txt"
FILTERS_CHECK_FILE_NAME_ONLYSpecifies that only the file name part of the file path is       to be checked against the Include and Exclude filter.
FILTERS_DEFAULT_BEHAVIORSpecifies the 'default' filter handling behaviour.
      Currently, it's only set to FILTERS_CHECK_FILE_NAME_ONLY.

      This implies that the Include/Exclude Filters are tested before On_FilterNotification,       and that On_WatchStarted and On_WatchStopped are called.

To specify these options, see the constructor  of CDirectoryChangeWatcher    , or use the function CDirectoryChangeWatcher::SetFilterFlags()  .

Note that the Filter Flags are used for the next call to CDirectoryChangeWatcher::WatchDirectory   and that calling CDirectoryChangeWatcher::SetFilterFlags() will   have no effect on any currently watched directories.

The sample app includes a dialog which allows you to test this out:

Thread Safety, and Message Pumps.

  While CDirectoryChangeWatcher uses a worker thread to get the job  done, all notifications are called in the context of the main thread . The 'main'  thread is the thread that first calls CDirectoryChangeWatcher::WatchDirectory  . CDirectoryChangeWatcher uses a hidden notification window to dispatch  notifications from the worker thread to the main thread. Because it uses a window,  the calling application must have a message pump implemented somewhere in the  program's 'main' thread.

Console Applications

For console applications, or applications/threads without a message pump,   CDirectoryChangeWatcher can still be used. Just pass false as   the value of the bAppHasGUI parameter to the constructor of CDirectoryChangeWatcher   . Instead of using a hidden notification window, CDirectoryChangeWatcher   uses an additional worker thread. Note that when you pass false as the value   of bAppHasGUI parameter to the constructor of CDirectoryChangeWatcher   , that all CDirectoryChangeHandler functions are called within   the context of a worker thread, and NOT the main thread.

CDirectoryChangeWatcher watcher(false); //safe to use in a console app.
				//
				//Note: CDirectoryChangeHandler functions are called 
				// 	in a worker thread.

A Sample:

class CMyDirectoryChangeHandler : public CDirectoryChangeHandler
{
public:
      	CMyDirectoryChangeHandler(){} 
      	virtual ~CMyDirectoryChangeHandler(){}

protected:
       	void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
       	{
             MessageBox(NULL, _T("The file ") + strOldFileName + _T(" was renamed to: ") + 
                              strNewFileName,_T("Test"),MB_OK);
       	}
       	bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName)
       	{ 
	   //
            // This programmer defined filter will only cause notifications
            // that a file name was changed to be sent.
            //
		if( dwNotifyAction == FILE_ACTION_RENAMED_OLD_NAME ) 
                   return true;
              	return false;
	}
};

....
        CDirectoryChangeWatcher watcher;
        CMyDirectoryChangeHandler MyChangeHandler;
        watcher.WatchDirectory(_T("C:\\Temp"), 
                                 FILE_CHANGE_NOTIFY_FILE_NAME,
                                 &MyChangeHandler,
                                 FALSE, //<-- watch sub directories? 
                                 NULL, //<-- Include Filter
                    		 NULL);//<-- Exclude Filter

Fin

CDirectoryChangeWatcher was based on the FWatch example program     in the SDK and uses an I/O completion port so that there will only be  one    worker thread (per instance of CDirectoryChangeWatcher    ) for   any number of watched directories.

When using this source code in your application, you only need to use CDirectoryChangeWatcher   and class(es) derived from CDirectoryChangeHandler.  Any other   classes in these source files are used to implement CDirectoryChangeWatcher.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值