This code wraps up the Win32 API function
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.
There are two classes that must be used together to watch a directory, they
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
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,
'dispatches' these notifications to your application via the
CDirectoryChangeHandler receives notifications
about file changes from
need to derive a class from
order for your application to handle the file change notifications.
A single instance of a
derived class can be used to handle notifications for multiple directory
watches at the same time, or you may specify different
derived classes for each watched directory.
The following is the public interface
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 |
CDirectoryChangeWatcher(bool bAppHasGUI = true,
DWORD dwFilterFlags = FILTERS_DEFAULT_BEHAVIOR);
DWORD WatchDirectory( const CString & strDirToWatch,
CDirectoryChangeHandler * pChangeHandler,
BOOL bWatchSubDirs = FALSE,
LPCTSTR szIncludeFilter = NULL,
LPCTSTR szExcludeFilter = NULL);
BOOL IsWatchingDirectory(const CString & strDirName)const;
BOOL UnwatchDirectory(const CString & strDirToStopWatching);
DWORD SetFilterFlags(DWORD dwFilterFlags);
DWORD GetFilterFlags() const;
CDirectoryChangeHandler has the following
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);
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
that does all of the things that you want to do when these events
There are 7 notifications that you can receive
when using these classes.
|Flag(s) required to receive notification--
|A directory watch has been started because |
|A directory watch has been stopped because |
Can also be called when
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.**
|Directory Watch Error|
|An error occurred while watching a directory.
In this condition, the watch is stopped automatically. You will not
receive the On_WatchStopped notification.|
| 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
|A file was deleted, or removed from the watched
directory(ie: sent to the recycle bin, moved to another directory, or deleted)|
|File Name Changed|
|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.|
|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.|
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
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
function is called.
The Include, Exclude filters:
The Include and Exclude filters are string parameters that
are passed to
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
"*.txt" <-- specifies only a single file pattern.
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
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
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()
On_FilterNotification returns true(the default),
you will receive the notification. If
returns false, the file notification is ignored. See the comments
in the source code for more information about this function.
There are several options related to how
works with filters.
|Specifies that the string filters for the Include and Exclude
filter are not checked. All notifications are sent unless |
|Specifies that |
is not tested. If the File name passes the test against the Include and
Exclude filter, the notification is passed on.
|Specifies that NO filter tests are to be performed. The
Include and Exclude filters are not tested, and |
is not called. This is a combination of FILTERS_DONT_USE_FILTERS and
CDirectoryChangeHandler::On_WatchStopped won't be called. Is
a combination of FILTERS_NO_WATCHSTART_NOTIFICATION and FILTERS_NO_WATCHSTOP_NOTIFICATION.
|Specifies that |
is to be called BEFORE the file name is tested against the Include and Exclude
filter patterns. The default is that
is tested AFTER only if the file name matches the patterns used that may
have been specified as Include or Exclude Filters.
|Specifies that the entire file name and path is to be tested
against the Include and Exclude Filter pattern.|
|Specifies 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
|Specifies that only the file name part of the file path is
to be checked against the Include and Exclude filter.|
|Specifies 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_WatchStopped are called.
To specify these options, see the constructor of
, or use the function
Note that the Filter Flags are used for the next call to
and that calling
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.
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
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.
For console applications, or applications/threads without a message pump,
can still be used. Just pass false as
the value of the bAppHasGUI parameter to the constructor of
. Instead of using a hidden notification window,
uses an additional worker thread. Note that when you pass false as the value
of bAppHasGUI parameter to the constructor of
, that all
CDirectoryChangeHandler functions are called within
the context of a worker thread, and NOT the main thread.
class CMyDirectoryChangeHandler : public CDirectoryChangeHandler
void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
MessageBox(NULL, _T("The file ") + strOldFileName + _T(" was renamed to: ") +
bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName)
if( dwNotifyAction == FILE_ACTION_RENAMED_OLD_NAME )
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
) for any number of watched directories.
When using this source code in your application, you only need to use
and class(es) derived from
CDirectoryChangeHandler. Any other
classes in these source files are used to implement
Download the sample or source code for more details.
CDirectoryChangeWatcher is based on the FWatch sample in the SDK.
- The sample application uses
CFolderDialog by Armen Hakobyan.
CDirectoryChangeWatcher uses a modified version of wildcmp
by Jack Handy
Feel free to email me with bugs, bug fixes, tips, comments, accolades, or admonitions