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