root/honeybow/trunk/mwwatcher/src/DirectoryChanges.cpp

Revision 670, 80.0 kB (checked in by chengyu, 2 years ago)

honeybow sensor mwwatcher component first public release.

Line 
1 // DirectoryChanges.cpp
2 //
3 // Implementation of the CDirectoryChangeWatcher and CDirectoryChangeHandler classes.
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
37 /************** Change Logs **********************************
38
39   Author:               Song Chengyu (songchengyu@icst.pku.edu.cn)
40  
41         Latest Changes:
42         
43           2006       -- Removed two function in class CDirectoryChangeHandler
44                                                 FtpInit, FtpPut
45
46                                         Modified FtpAddFile in class CDirectoryChangeHandler
47
48                                         Added three function in class CDirectoryChangeHandler
49                                                 isInBlackList, isInWhiteList, On_FileUploaded
50
51                                                 
52           
53           12/01/2005 -- Added three function in class CDirecotryChangeHandler which handles
54                         the file upload fears
55           
56                                                 FtpInit -- Initialize the command header
57                                                 FtpAddFile -- Put files needed to be uploaded in a CString
58                                                 FtpPut -- Finish the command string, apply a temp file,
59                                                   write the source file that will be used by the "ftp -s:"
60                                                   command
61                                                   
62                                         Added some global vars in seek of auto-monitor when system starts
63
64   ***********************************************************
65
66   Author:       Wes Jones wesj@hotmail.com
67
68   File:         DirectoryChanges.cpp
69
70   Latest Changes:
71
72                 11/22/2001      --      Fixed bug causing file name's to be truncated if
73                                                 longer than 130 characters. Fixed CFileNotifyInformation::GetFileName()
74                                                 Thanks to Edric(uo_edric@hotmail.com) for pointing this out.
75
76                                                 Added code to enable process privileges when CDirectoryChangeWatcher::WatchDirectory()
77                                                 is first called.        See docuementation API for ReadDirectoryChangesW() for more information of required privileges.
78                                                 
79                                                   Currently enables these privileges: (11/22/2001)
80                                                         SE_BACKUP_NAME
81                                                         SE_CHANGE_NOTIFY_NAME
82                                                         SE_RESTORE_NAME(02/09/2002)
83                                                 Implemented w/ helper class CPrivilegeEnabler.
84
85                 11/23/2001              Added classes so that all notifications are handled by the
86                                                 same thread that called CDirectoryChangeWatcher::WatchDirectory(),
87                                                 ie: the main thread.
88                                                 CDirectoryChangeHandler::On_Filexxxx() functions are now called in the
89                                                 context of the main thread instead of the worker thread.
90
91                                                 This is good for two reasons:
92                                                 1: The worker thread spends less time handling each notification.
93                                                         The worker thread simply passes the notification to the main thread,
94                                                         which does the processing.
95                                                         This means that each file change notification is handled as fast as possible
96                                                         and ReadDirectoryChangesW() can be called again to receive more notifications
97                                                         faster.
98
99                                                 2:      This makes it easier to make an ActiveX or ATL object with this class
100                                                         because the events that are fired, fire in the context of the main thread.
101                                                         The fact that I'm using a worker thread w/ this class is totally
102                                                         transparent to a client user.
103                                                         If I decide to turn this app into an ActiveX or ATL object
104                                                         I don't have to worry about wierd COM rules and multithreading issues,
105                                                         and neither does the client, be the client a VB app, C++ app, Delphi app, or whatever.
106
107                                                 Implemented with CDelayedDirectoryChangeHandler in DirectoryChangeHandler.h/.cpp
108
109                 02/06/2002        Fixed a bug that would cause an application to hang.
110                                                 If ReadDirectoryChangesW was to fail during normal operation,
111                                                 the short story is that the application would hang
112                                                 when it called CDirectoryChangeWatcher::UnwatchDirectory(const CString & )
113
114                                                 One way to reproduce this behavior on the old code
115                                                 is to watch a directory using a UNC path, and then change the IP
116                                                 address of that machine while the watch was running.  Exitting
117                                                 the app after this would cause it to hang.
118
119                                                 Steps to reproduce it:
120
121                                                 1) Assume that the computer running the code is
122                                                         named 'ThisComputer' and there is a shared folder named 'FolderName'
123
124
125                                                 2) Start a watch on a folder using a UNC path:  ie: \\ThisComputer\FolderName
126                                                 
127                                                   eg:   CDirectoryChangeWatcher watcher;
128                                                         
129                                                                 watcher.WatchDirectory(_T("\\\\ThisComputer\\FolderName",/ * other parameters * /)
130                                                         
131                                                 3)  Change the IP address of 'ThisComputer'
132
133                                                            ** ReadDirectoryChangesW() will fail some point after this.
134                                                 
135                 
136                                                 4)  Exit the application... it may hang.
137
138
139                                                 Anyways, that's what the bug fix is for.
140
141
142                 02/06/2002      New side effects for CDirectoryChangeHandler::On_ReadDirectoryChangeError()
143
144                                         If CDirectoryChangeHandler::On_ReadDirectoryChangeError() is ever executed
145                                         the directory that you are watching will have been unwatched automatically due
146                                         to the error condition.
147
148                                         A call to CDirectoryChangeWatcher::IsWatchingDirectory() will fail because the directory
149                                         is no longer being watched. You'll need to re-watch that directory.
150
151                 02/09/2002      Added a parameter to CDirectoryChangeHandler::On_ReadDirectoryChangeError()
152
153                                         Added the parameter: const CString & strDirectoryName
154                                         The new signature is now:
155                                                         virtual void CDirectoryChangeHandler::On_ReadDirectoryChangeError(DWORD dwError, const CString & strDirectoryName);
156
157                                         This new parameter gives you the name of the directory that the error occurred on.
158
159                 04/25/2002  Provided a way to get around the requirement of a message pump.
160                                         A console app can now use this w/out problems by passing false
161                                         to the constructor of CDirectoryChangeWatcher.
162                                         An app w/ a message pump can also pass false if it so desires w/out problems.
163
164                 04/25/2002      Added two new virtual functions to CDirectoryChangeHandler
165
166                                         Added:
167                                                         On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
168                                                         On_WatchStopped(const CString & strDirectoryName);
169                                         See header file for details.
170
171                 04/27/2002      Added new function to CDirectoryChangeHandler:
172
173                                         Added virtual bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName)
174
175                                         This function is called before any notification function, and allows the
176                                         CDirectoryChangeHandler derived class to ignore file notifications
177                                         by performing a programmer defined test.
178                                         By ignore, i mean that
179                                                 On_FileAdded(), On_FileRemoved(), On_FileModified(), or On_FileNameChanged()
180                                                 will NOT be called if this function returns false.
181
182                                         The default implementation always returns true, signifying that ALL notifications
183                                         are to be called.
184
185
186                 04/27/2002  Added new Parameters to CDirectoryChangeWatcher::WatchDirectory()
187
188                                         The new parameters are:
189                                                                                 LPCTSTR szIncludeFilter
190                                                                                 LPCTSTR szExcludeFilter
191                                         Both parameters are defaulted to NULL.
192                                         Signature is now:
193                                         CDirectoryChangeWatcher::WatchDirectory(const CString & strDirToWatch,
194                                                                                                                         DWORD dwChangesToWatchFor,
195                                                                                                                         CDirectoryChangeHandler * pChangeHandler,
196                                                                                                                         BOOL bWatchSubDirs = FALSE,
197                                                                                                                         LPCTSTR szIncludeFilter = NULL,
198                                                                                                                         LPCTSTR szExcludeFilter = NULL)
199
200                 04/27/2002      Added support for include and exclude filters.
201                                         These filters allow you to receive notifications for just the files you
202                                         want... ie: you can specify that you only want to receive notifications
203                                         for changes to "*.txt" files or some other such file filter.
204
205                                         NOTE: This feature is implemented w/ the PathMatchSpec() api function
206                                         which is only available on NT4.0 if Internet Explorer 4.0 or above is installed.
207                                         See MSDN for PathMatchSpec().  Win2000, and XP do not have to worry about it.
208
209                                         Filter specifications:
210                                            Accepts wild card characters * and ?, just as you're used to for the DOS dir command.
211                                            It is possible to specify multiple extenstions in the filter by separating each filter spec
212                                            with a semi-colon.
213                                            eg: "*.txt;*.tmp;*.log"  <-- this filter specifies all .txt, .tmp, & .log files
214
215                                         
216
217                                         Filters are passed as parameters to CDirectoryChangeWatcher::WatchDirectory()
218
219                                         NOTE: The filters you specify take precedence over CDirectoryChangeHandler::On_FilterNotification().
220                                                   This means that if the file name does not pass the filters that you specify
221                                                   when the watch is started, On_FilterNotification() will not be called.
222                                                   Filter specifications are case insensitive, ie: ".tmp" and ".TMP" are the same
223
224
225                                         FILTER TYPES:
226                                                         Include Filter:
227                                                                                 If you specify an include filter, you are specifying that you
228                                                                                 only want to receive notifications for specific file types.
229                                                                                 eg: "*.log" means that you only want notifications for changes
230                                                                                         to files w/ an exention of ".log".
231                                                                                         Changes to ALL OTHER other file types are ignored.
232                                                                                 An empty, or not specified include filter means that you want
233                                                                                 notifications for changes of ALL file types.
234                                                         Exclude filter:
235
236                                                                                 If you specify an exclude filter, you are specifying that
237                                                                                 you do not wish to receive notifications for a specific type of file or files.
238                                                                                 eg: "*.tmp" would mean that you do not want any notifications
239                                                                                         regarding files that end w/ ".tmp"
240                                                                                 An empty, or not specified exclude filter means that
241                                                                                 you do not wish to exclude any file notifications.
242
243
244
245
246
247                 
248                                         
249 ************************************************************/
250
251 #include "stdafx.h"
252 #include "DirectoryChanges.h"
253 #include "DelayedDirectoryChangeHandler.h"
254 #include <list>
255 #include <shlwapi.h>
256
257 using namespace std;
258
259 #ifdef _DEBUG
260 #undef THIS_FILE
261 static char THIS_FILE[]=__FILE__;
262 #define new DEBUG_NEW
263 #endif
264
265 //
266 //
267 //      Fwd Declares & #define's
268 //
269 //
270 //
271 // Helper classes
272 class CPrivilegeEnabler;         //for use w/ enabling process priveledges when this code is first used. It's a singleton.
273
274 class CFileNotifyInformation;//helps CDirectoryChangeWatcher class notify CDirectoryChangeHandler class of changes to files.
275
276 class CDelayedDirectoryChangeHandler;   //      Helps all notifications become handled by the main
277                                                                                 //      thread, or a worker thread depending upon a value passed to the
278                                                                                 //      constructor of CDirectoryChangeWatcher.
279                                                                                 //     
280                                                                                 //      For notifications to fire in the main thread, your app must have a message pump.
281                                                                                 //
282                                                                                 //  The 'main' thread is the one that called CDirectoryChangeWatcher::WatchDirectory()
283
284 // Helper functions
285 static BOOL     EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable = TRUE);
286 static bool IsDirectory(const CString & strPath);
287
288 //CString strGFtpAddress;
289 //CString strGFtpUserName;
290 //CString strGFtpUserPasswd;
291
292 extern list<CString>  AddedFiles;
293 extern HANDLE   hGMutex;
294 extern BOOL             bGSetFilter;
295
296 /////////////////////////////////////////////////////////////////////
297 //      Helper functions.
298 BOOL    EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable /*= TRUE*/)
299 //
300 //      I think this code is from a Jeffrey Richter book...
301 //
302 //      Enables user priviledges to be set for this process.
303 //     
304 //      Process needs to have access to certain priviledges in order
305 //      to use the ReadDirectoryChangesW() API.  See documentation.
306 {   
307         BOOL fOk = FALSE;   
308         // Assume function fails   
309         HANDLE hToken;   
310         // Try to open this process's access token   
311         if (OpenProcessToken(GetCurrentProcess(),               
312                                         TOKEN_ADJUST_PRIVILEGES, &hToken))     
313         {       
314                 // privilege       
315                 TOKEN_PRIVILEGES tp = { 1 };       
316
317                 if( LookupPrivilegeValue(NULL, pszPrivName,  &tp.Privileges[0].Luid) )
318                 {
319                         tp.Privileges[0].Attributes = fEnable ?  SE_PRIVILEGE_ENABLED : 0;
320
321                         AdjustTokenPrivileges(hToken, FALSE, &tp,                             
322                                                                         sizeof(tp), NULL, NULL);
323
324                         fOk = (GetLastError() == ERROR_SUCCESS);               
325                 }
326                 CloseHandle(hToken);   
327         }       
328         return(fOk);
329 }
330
331 static bool IsDirectory(const CString & strPath)
332 //
333 //  Returns: bool
334 //              true if strPath is a path to a directory
335 //              false otherwise.
336 //
337 {
338         DWORD dwAttrib  = GetFileAttributes( strPath );
339         return static_cast<bool>( ( dwAttrib != 0xffffffff
340                                                         &&      (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) );
341
342                
343 }
344 ///////////////////////////////////////////////
345 //Helper class:
346
347 class CFileNotifyInformation
348 /*******************************
349
350 A Class to more easily traverse the FILE_NOTIFY_INFORMATION records returned
351 by ReadDirectoryChangesW().
352
353 FILE_NOTIFY_INFORMATION is defined in Winnt.h as:
354
355  typedef struct _FILE_NOTIFY_INFORMATION {
356     DWORD NextEntryOffset;
357         DWORD Action;
358     DWORD FileNameLength;
359     WCHAR FileName[1];
360 } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;   
361
362   ReadDirectoryChangesW basically puts x amount of these records in a
363   buffer that you specify.
364   The FILE_NOTIFY_INFORMATION structure is a 'dynamically sized' structure (size depends on length
365   of the file name (+ sizeof the DWORDs in the struct))
366
367   Because each structure contains an offset to the 'next' file notification
368   it is basically a singly linked list.  This class treats the structure in that way.
369  
370
371   Sample Usage:
372   BYTE Read_Buffer[ 4096 ];
373
374   ...
375   ReadDirectoryChangesW(...Read_Buffer, 4096,...);
376   ...
377
378   CFileNotifyInformation notify_info( Read_Buffer, 4096);
379   do{
380             switch( notify_info.GetAction() )
381                 {
382                 case xx:
383                     notify_info.GetFileName();
384                 }
385
386   while( notify_info.GetNextNotifyInformation() );
387  
388 ********************************/
389 {
390 public:
391         CFileNotifyInformation( BYTE * lpFileNotifyInfoBuffer, DWORD dwBuffSize)
392         : m_pBuffer( lpFileNotifyInfoBuffer ),
393           m_dwBufferSize( dwBuffSize )
394         {
395                 ASSERT( lpFileNotifyInfoBuffer && dwBuffSize );
396                
397                 m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) m_pBuffer;
398         }
399
400        
401         BOOL GetNextNotifyInformation();
402        
403         BOOL CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord);
404
405         DWORD   GetAction() const;//gets the type of file change notifiation
406         CString GetFileName()const;//gets the file name from the FILE_NOTIFY_INFORMATION record
407         CString GetFileNameWithPath(const CString & strRootPath) const;//same as GetFileName() only it prefixes the strRootPath into the file name
408
409        
410 protected:
411         BYTE * m_pBuffer;//<--all of the FILE_NOTIFY_INFORMATION records 'live' in the buffer this points to...
412         DWORD  m_dwBufferSize;
413         PFILE_NOTIFY_INFORMATION m_pCurrentRecord;//this points to the current FILE_NOTIFY_INFORMATION record in m_pBuffer
414         
415 };
416
417 BOOL CFileNotifyInformation::GetNextNotifyInformation()
418 /***************
419   Sets the m_pCurrentRecord to the next FILE_NOTIFY_INFORMATION record.
420
421   Even if this return FALSE, (unless m_pCurrentRecord is NULL)
422   m_pCurrentRecord will still point to the last record in the buffer.
423 ****************/
424 {
425         if( m_pCurrentRecord
426         &&      m_pCurrentRecord->NextEntryOffset != 0UL)//is there another record after this one?
427         {
428                 //set the current record to point to the 'next' record
429                 PFILE_NOTIFY_INFORMATION pOld = m_pCurrentRecord;
430                 m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) ((LPBYTE)m_pCurrentRecord + m_pCurrentRecord->NextEntryOffset);
431
432                 ASSERT( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) < m_dwBufferSize);//make sure we haven't gone too far
433
434                 if( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) > m_dwBufferSize )
435                 {
436                         //we've gone too far.... this data is hosed.
437                         //
438                         // This sometimes happens if the watched directory becomes deleted... remove the FILE_SHARE_DELETE flag when using CreateFile() to get the handle to the directory...
439                         m_pCurrentRecord = pOld;
440                 }
441                                        
442                 return (BOOL)(m_pCurrentRecord != pOld);
443         }
444         return FALSE;
445 }
446
447 BOOL CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord)
448 /*****************************************
449    Copies the FILE_NOTIFY_INFORMATION record to the beginning of the buffer
450    specified in the constructor.
451
452    The size of the current record is returned in DWORD & dwSizeOfCurrentRecord.
453   
454 *****************************************/
455 {
456         ASSERT( m_pBuffer && m_pCurrentRecord );
457         if( !m_pCurrentRecord ) return FALSE;
458
459         BOOL bRetVal = TRUE;
460
461         //determine the size of the current record.
462         ref_dwSizeOfCurrentRecord = sizeof( FILE_NOTIFY_INFORMATION );
463         //subtract out sizeof FILE_NOTIFY_INFORMATION::FileName[1]
464         WCHAR FileName[1];//same as is defined for FILE_NOTIFY_INFORMATION::FileName
465         UNREFERENCED_PARAMETER(FileName);
466         ref_dwSizeOfCurrentRecord -= sizeof(FileName);   
467         //and replace it w/ value of FILE_NOTIFY_INFORMATION::FileNameLength
468         ref_dwSizeOfCurrentRecord += m_pCurrentRecord->FileNameLength;
469
470         ASSERT( (DWORD)((LPBYTE)m_pCurrentRecord + ref_dwSizeOfCurrentRecord) <= m_dwBufferSize );
471
472         ASSERT( (void*)m_pBuffer != (void*)m_pCurrentRecord );//if this is the case, your buffer is way too small
473         if( (void*)m_pBuffer != (void*) m_pCurrentRecord )
474         {//copy the m_pCurrentRecord to the beginning of m_pBuffer
475                 
476                 ASSERT( (DWORD)m_pCurrentRecord > (DWORD)m_pBuffer + ref_dwSizeOfCurrentRecord);//will it overlap?
477                 __try{
478                         memcpy(m_pBuffer, m_pCurrentRecord, ref_dwSizeOfCurrentRecord);
479                         bRetVal = TRUE;
480                 }
481                 __except(EXCEPTION_EXECUTE_HANDLER)
482                 {
483                         TRACE(_T("EXCEPTION!  CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer() -- probably because bytes overlapped in a call to memcpy()"));
484                         bRetVal = FALSE;
485                 }
486         }
487         //else
488         //there was only one record in this buffer, and m_pCurrentRecord is already at the beginning of the buffer
489         return bRetVal;
490 }
491
492 DWORD CFileNotifyInformation::GetAction() const
493 {
494         ASSERT( m_pCurrentRecord );
495         if( m_pCurrentRecord )
496                 return m_pCurrentRecord->Action;
497         return 0UL;
498 }
499
500 CString CFileNotifyInformation::GetFileName() const
501 {
502         //
503         //BUG FIX:
504         //              File Name's longer than 130 characters are truncated
505         //
506         //              Thanks Edric @ uo_edric@hotmail.com for pointing this out.
507         if( m_pCurrentRecord )
508         {
509                 WCHAR wcFileName[ MAX_PATH + 1] = {0};//L"";
510                 memcpy( wcFileName,
511                                 m_pCurrentRecord->FileName,
512                                 //min( MAX_PATH, m_pCurrentRecord->FileNameLength) <-- buggy line
513                                 min( (MAX_PATH * sizeof(WCHAR)), m_pCurrentRecord->FileNameLength));
514                
515
516                 return CString( wcFileName );
517         }
518         return CString();
519 }               
520
521 static inline bool HasTrailingBackslash(const CString & str )
522 {
523         if( str.GetLength() > 0
524         &&      str[ str.GetLength() - 1 ] == _T('\\') )
525                 return true;
526         return false;
527 }
528 CString CFileNotifyInformation::GetFileNameWithPath(const CString & strRootPath) const
529 {
530         CString strFileName( strRootPath );
531         //if( strFileName.Right(1) != _T("\\") )
532         if( !HasTrailingBackslash( strRootPath ) )
533                 strFileName += _T("\\");
534
535         strFileName += GetFileName();
536         return strFileName;
537 }
538 /////////////////////////////////////////////////////////////////////////////////
539 class CPrivilegeEnabler
540 //
541 //      Enables privileges for this process
542 //      first time CDirectoryChangeWatcher::WatchDirectory() is called.
543 //
544 //      It's a singleton.
545 //
546 {
547 private:
548         CPrivilegeEnabler();//ctor
549 public:
550         ~CPrivilegeEnabler(){};
551        
552         static CPrivilegeEnabler & Instance();
553         //friend static CPrivilegeEnabler & Instance();
554 };
555
556 CPrivilegeEnabler::CPrivilegeEnabler()
557 {
558         LPCTSTR arPrivelegeNames[]      =       {
559                                                                                 SE_BACKUP_NAME, //      these two are required for the FILE_FLAG_BACKUP_SEMANTICS flag used in the call to
560                                                                                 SE_RESTORE_NAME,//  CreateFile() to open the directory handle for ReadDirectoryChangesW
561
562                                                                                 SE_CHANGE_NOTIFY_NAME //just to make sure...it's on by default for all users.
563                                                                                 //<others here as needed>
564                                                                         };
565         for(int i = 0; i < sizeof(arPrivelegeNames) / sizeof(arPrivelegeNames[0]); ++i)
566         {
567                 if( !EnablePrivilege(arPrivelegeNames[i], TRUE) )
568                 {
569                         TRACE(_T("Unable to enable privilege: %s        --      GetLastError(): %d\n"), arPrivelegeNames[i], GetLastError());
570                         TRACE(_T("CDirectoryChangeWatcher notifications may not work as intended due to insufficient access rights/process privileges.\n"));
571                         TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__);
572                 }
573         }
574 }
575
576 CPrivilegeEnabler & CPrivilegeEnabler::Instance()
577 {
578         static CPrivilegeEnabler theInstance;//constructs this first time it's called.
579         return theInstance;
580 }
581 //
582 //
583 //
584 ///////////////////////////////////////////////////////////
585
586        
587 //
588 //
589 //////////////////////////////////////////////////////////////////////
590 // Construction/Destruction
591 //////////////////////////////////////////////////////////////////////
592 CDirectoryChangeHandler::CDirectoryChangeHandler()
593 : m_nRefCnt( 1 ),
594   m_pDirChangeWatcher( NULL ),
595   m_nWatcherRefCnt( 0L )
596 {
597 }
598
599 CDirectoryChangeHandler::~CDirectoryChangeHandler()
600 {
601         UnwatchDirectory();
602 }
603
604 long CDirectoryChangeHandler::AddRef()
605 {
606         return InterlockedIncrement(&m_nRefCnt);       
607 }
608
609 long CDirectoryChangeHandler::Release()
610
611         long nRefCnt = -1;
612         if( (nRefCnt = InterlockedDecrement(&m_nRefCnt)) == 0 )
613                 delete this;
614         return nRefCnt;
615 }
616 long CDirectoryChangeHandler::CurRefCnt()const
617 {
618         return m_nRefCnt;
619 }
620
621 BOOL CDirectoryChangeHandler::UnwatchDirectory()
622 {
623         CSingleLock lock(&m_csWatcher, TRUE);   
624         ASSERT( lock.IsLocked() );
625        
626         if( m_pDirChangeWatcher )
627                 return m_pDirChangeWatcher->UnwatchDirectory( this );
628         return TRUE;
629 }
630
631 long  CDirectoryChangeHandler::ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher)
632 {
633         ASSERT( pDirChangeWatcher );
634         CSingleLock lock(&m_csWatcher, TRUE);
635         if( m_pDirChangeWatcher
636         &&  m_pDirChangeWatcher != pDirChangeWatcher )
637         {
638                 TRACE(_T("CDirectoryChangeHandler...is becoming used by a different CDirectoryChangeWatcher!\n"));
639                 TRACE(_T("Directories being handled by this object will now be unwatched.\nThis object is now being used to ")
640                           _T("handle changes to a directory watched by different CDirectoryChangeWatcher object, probably on a different directory"));
641                
642                 if( UnwatchDirectory() )