1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
// DelayedDirectoryChangeHandler.h: interface for the CDelayedDirectoryChangeHandler2 class.
//
//////////////////////////////////////////////////////////////////////
//
// You needn't worry about the classes in this file.
// they are implementation classes used to help CDirectoryChangeWatcher work.
//
//
#if !defined(AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_)
#define AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//classes declrared in other files:
class CDirectoryChangeWatcher;
class CDirectoryChangeHandler;
//classes declared in this file:
class CDirChangeNotification;
class CDelayedDirectoryChangeHandler;
class CDelayedNotificationWindow;
class CDelayedNotificationThread;
/*******************************************************************
The classes in this file implement methods to ensure that file change
notifications are fired in a thread other than the worker thread used
by CDirectoryChangeWatcher.
Dispatching the notifications in to a different thread improves the performance
of CDirectoryChangeWatcher so that it can process more notifications faster
and notifications aren't 'lost'.
There are two methods of dispatching functions to another thread.
1) One is to use the message pump associated w/ the main thread by posting notifications
to a hidden window. This is implemented w/ the class CDelayedNotificationWindow.
2) The other is to create a worker thread that implements a message pump. This is
implemented w/ the class CDelayedNotificationThread.
If your app uses a GUI then it has a already has message pump.
You can make sure that CDelayedNotificationWindow is used in this case.
The advantage here being that there is one less worker thread used in your program.
If your app is a command line app or otherwise doesn't have a GUI,
then you will want to make sure that you are using the CDelayedNotificationThread
to dispatch notifications to another thread.
This is determined by a flag passed is passed to the constructor of CDirecotryChangeWatcher
********************************************************************/
class CDelayedNotifier
//
// Abstract base class for ensuring notifications are fired in a thread
//
//
{
public:
virtual ~CDelayedNotifier(){}
virtual void PostNotification(CDirChangeNotification * pNotification) = 0;
};
class CDelayedNotificationWindow : public CDelayedNotifier
//
// A class that implements a
// there will always be only one of the actual windows
// in existance.
//
{
public:
CDelayedNotificationWindow(){ AddRef(); }
virtual ~CDelayedNotificationWindow(){ Release(); }
void PostNotification(CDirChangeNotification * pNotification);
private:
long AddRef(); // the window handle is reference counted
long Release(); //
static long s_nRefCnt;
static HWND s_hWnd; //there's only one window no matter how many instances of this class there are.... this means that all notifications are handled by the same thread.
static BOOL s_bRegisterWindow;
BOOL RegisterWindowClass(LPCTSTR szClassName);
BOOL CreateNotificationWindow();
};
class CDelayedNotificationThread : public CDelayedNotifier
//
// Class that implements a worker thread w/ a message pump.
// CDirectoryChangeWatcher posts notifications to this thread, where they are dispatched.
// This thread executes CDirectoryChangeHandler notifications.
//
{
public:
CDelayedNotificationThread()
:m_hThreadStartEvent(NULL)
{
m_hThreadStartEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
ASSERT( m_hThreadStartEvent );
AddRef();
}
virtual ~CDelayedNotificationThread()
{
Release();
if( m_hThreadStartEvent )
CloseHandle(m_hThreadStartEvent), m_hThreadStartEvent = NULL;
}
void PostNotification(CDirChangeNotification * pNotification);
private:
long AddRef(); // The thread handle is reference
long Release(); // counted so that only one thread is used
// so that there's only one worker thread(performing this functino)
static long s_nRefCnt; // no matter how many directories are being watched
static HANDLE s_hThread; //
static DWORD s_dwThreadID; //
static UINT __stdcall ThreadFunc(LPVOID lpvThis);
bool StartThread();
bool StopThread();
BOOL WaitForThreadStartup(){ return WaitForSingleObject(m_hThreadStartEvent, INFINITE) == WAIT_OBJECT_0; };
BOOL SignalThreadStartup(){ return SetEvent( m_hThreadStartEvent ) ; }
HANDLE m_hThreadStartEvent;//signals that the worker thread has started. this fixes a bug condition.
};
class CDirChangeNotification
//
// A class to help dispatch the change notifications to the main thread.
//
// This class holds the data in memory until the notification can be dispatched.(ie: this is the time between when the notification is posted, and the clients notification code is called).
//
//
{
private:
CDirChangeNotification();//not implemented
public:
explicit CDirChangeNotification(CDelayedDirectoryChangeHandler * pDelayedHandler, DWORD dwPartialPathOffset);
~CDirChangeNotification();
//
//
void PostOn_FileAdded(LPCTSTR szFileName);
void PostOn_FileRemoved(LPCTSTR szFileName);
void PostOn_FileNameChanged(LPCTSTR szOldName, LPCTSTR szNewName);
void PostOn_FileModified(LPCTSTR szFileName);
void PostOn_ReadDirectoryChangesError(DWORD dwError, LPCTSTR szDirectoryName);
void PostOn_WatchStarted(DWORD dwError, LPCTSTR szDirectoryName);
void PostOn_WatchStopped(LPCTSTR szDirectoryName);
void DispatchNotificationFunction();
enum eFunctionToDispatch{ eFunctionNotDefined = -1,
eOn_FileAdded = FILE_ACTION_ADDED,
eOn_FileRemoved = FILE_ACTION_REMOVED,
eOn_FileModified = FILE_ACTION_MODIFIED,
eOn_FileNameChanged = FILE_ACTION_RENAMED_OLD_NAME,
eOn_ReadDirectoryChangesError,
eOn_WatchStarted,
eOn_WatchStopped
};
protected:
void PostNotification();
private:
friend class CDelayedDirectoryChangeHandler;
CDelayedDirectoryChangeHandler * m_pDelayedHandler;
//
// Members to help implement DispatchNotificationFunction
//
//
eFunctionToDispatch m_eFunctionToDispatch;
//Notification Data:
TCHAR * m_szFileName1;//<-- is the szFileName parameter to On_FileAdded(),On_FileRemoved,On_FileModified(), and is szOldFileName to On_FileNameChanged(). Is also strDirectoryName to On_ReadDirectoryChangesError(), On_WatchStarted(), and On_WatchStopped()
TCHAR * m_szFileName2;//<-- is the szNewFileName parameter to On_FileNameChanged()
DWORD m_dwError; //<-- is the dwError parameter to On_WatchStarted(), and On_ReadDirectoryChangesError()
//
DWORD m_dwPartialPathOffset;//helps support FILTERS_CHECK_PARTIAL_PATH...not passed to any functions other than may be used during tests in CDelayedDirectoryChangeHandler::NotifyClientOfFileChange()
friend class CDirChangeNotification;
friend class CDirectoryChangeWatcher;
friend DWORD GetPathOffsetBasedOnFilterFlags(CDirChangeNotification*,DWORD);//a friend function
};
//////////////////////////////////////////////////////////////////////////
//
// This class makes it so that a file change notification is executed in the
// context of the main thread, and not the worker thread.
//
//
// It works by creating a hidden window. When it receieves a notification
// via one of the On_Filexxx() functions, a message is posted to this window.
// when the message is handled, the notification is fired again in the context
// of the main thread, or whichever thread that called CDirectoryChangeWatcher::WatchDirectory()
//
//
/////////////////////////////////////////////////////////////////////////////
// Note this code wants to use PathMatchSpec()
// which is only supported on WINNT 4.0 w/ Internet Explorer 4.0 and above.
// PathMatchSpec is fully supported on Win2000/XP.
//
// For the case of WINNT 4.0 w/out IE 4.0, we'll use a simpler function.
// some functionality is lost, but such is the price.
//
typedef BOOL (STDAPICALLTYPE * FUNC_PatternMatchSpec)(LPCTSTR pszFile, LPCTSTR pszSpec);
class CDelayedDirectoryChangeHandler : public CDirectoryChangeHandler
//
// Decorates an instance of a CDirectoryChangeHandler object.
// Intercepts notification function calls and posts them to
// another thread through a method implemented by a class derived from
// CDelayedNotifier
//
//
// This class implements dispatching the notifications to a thread
// other than CDirectoryChangeWatcher::MonitorDirectoryChanges()
//
// Also supports the include and exclude filters for each directory
//
{
private:
CDelayedDirectoryChangeHandler();//not implemented.
public:
CDelayedDirectoryChangeHandler( CDirectoryChangeHandler * pRealHandler, bool bAppHasGUI, LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter, DWORD dwFilterFlags);
virtual ~CDelayedDirectoryChangeHandler();
CDirectoryChangeHandler * GetRealChangeHandler()const { return m_pRealHandler; }
CDirectoryChangeHandler * & GetRealChangeHandler(){ return m_pRealHandler; }//FYI: PCLint will give a warning that this exposes a private/protected member& defeats encapsulation.
void PostNotification(CDirChangeNotification * pNotification);
void DispatchNotificationFunction(CDirChangeNotification * pNotification);
protected:
//These functions are called when the directory to watch has had a change made to it
void On_FileAdded(const CString & strFileName);
void On_FileRemoved(const CString & strFileName);
void On_FileModified(const CString & strFileName);
void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName);
void On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName);
void On_WatchStarted(DWORD dwError, const CString & strDirectoryName);
void On_WatchStopped(const CString & strDirectoryName);
void SetChangedDirectoryName(const CString & strChangedDirName);
const CString & GetChangedDirectoryName()const;
BOOL WaitForOnWatchStoppedDispatched();//see comments in .cpp
bool NotifyClientOfFileChange(CDirChangeNotification * pNot);
bool IncludeThisNotification(LPCTSTR szFileName); // based on file name.
bool ExcludeThisNotification(LPCTSTR szFileName); // Allows us to filter notifications
//
CDirChangeNotification * GetNotificationObject();
void DisposeOfNotification(CDirChangeNotification * pNotification);
CDelayedNotifier * m_pDelayNotifier;
CDirectoryChangeHandler * m_pRealHandler;
// m_bAppHasGUI:
// This flag, if set to true, indicates that the app has a message
bool m_bAppHasGUI; // pump, and that functions are dispatched to the main thread.
// Otherwise, functions are dispatched to a separate worker thread.
//
DWORD m_dwFilterFlags;
DWORD m_dwPartialPathOffset; //helps support FILTERS_CHECK_PARTIAL_PATH
void SetPartialPathOffset(const CString & strWatchedDirName);
friend class CDirectoryChangeWatcher;
friend class CDirectoryChangeWatcher::CDirWatchInfo;
private:
HANDLE m_hWatchStoppedDispatchedEvent;//supports WaitForOnWatchStoppedDispatched()
TCHAR * m_szIncludeFilter; // Supports the include
TCHAR * m_szExcludeFilter; // & exclude filters
//
// Load PathMatchSpec dynamically because it's only supported if IE 4.0 or greater is
// installed.
static HMODULE s_hShlwapi_dll;//for the PathMatchSpec() function
static BOOL s_bShlwapi_dllExists;//if on NT4.0 w/out IE 4.0 or greater, this'll be false
static long s_nRefCnt_hShlwapi;
static FUNC_PatternMatchSpec s_fpPatternMatchSpec;
BOOL _PathMatchSpec(LPCTSTR szPath, LPCTSTR szPattern);
BOOL InitializePathMatchFunc(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter);
BOOL InitializePatterns(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter);
void UninitializePathMatchFunc();
bool UsesRealPathMatchSpec() const;//are we using PathMatchSpec() or wildcmp()?
//note: if the PathMatchSpec function isn't found, wildcmp() is used instead.
//
// to support multiple file specs separated by a semi-colon,
// the include and exclude filters that are passed into the
// the constructor are parsed into separate strings
// which are all checked in a loop.
//
int m_nNumIncludeFilterSpecs;
int m_nNumExcludeFilterSpecs;
};
#endif // !defined(AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_)
|