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

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

honeybow sensor mwwatcher component first public release.

Line 
1 // DelayedDirectoryChangeHandler.cpp
2 //
3 // implementation of 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 #include "stdafx.h"
37 #include "DirectoryChanges.h"
38 #include "DelayedDirectoryChangeHandler.h"
39 #include <process.h>//for _beginthreadex
40
41 #include <shlwapi.h>                             // for PathMatchSpec
42 #pragma comment( lib, "shlwapi.lib") // function
43
44
45 #ifdef _DEBUG
46 #undef THIS_FILE
47 static char THIS_FILE[]=__FILE__;
48 #define new DEBUG_NEW
49 #endif
50
51
52 #define UWM_DELAYED_DIRECTORY_NOTIFICATION (WM_APP+1024)
53
54 extern CString strGFtpAddress;
55 extern CString strGFtpUserName;
56 extern CString strGFtpUserPasswd;
57
58 HINSTANCE GetInstanceHandle()
59 {
60         return (HINSTANCE)GetModuleHandle(NULL);
61         // ASSERT( AfxGetInstanceHandle() == (HINSTANCE)GetModuleHandle(NULL) ); <-- true for building .exe's
62         //NOTE: In Dll's using shared MFC, AfxGetInstanceHandle() != (HINSTANCE)GetModuleHandle(NULL)...
63         //don't know if this is the case for dll's using static MFC
64 }
65 static inline bool IsEmptyString(LPCTSTR sz)
66 {
67         return (bool)(sz==NULL || *sz == 0);
68 }
69 /*********************************************************
70   PathMatchSpec() requires IE 4.0 or greater on NT...
71   if running on NT 4.0 w/ out IE 4.0, then uses this function instead.
72
73   Based on code by Jack Handy:
74   http://www.codeproject.com/string/wildcmp.asp
75
76   Changed slightly to match the PathMatchSpec signature, be unicode compliant & to ignore case by myself.
77  
78 *********************************************************/
79
80 #define _TESTING_WILDCMP_ONLY_
81
82 BOOL STDAPICALLTYPE wildcmp(LPCTSTR string, LPCTSTR wild )
83 {
84         const TCHAR *cp, *mp;
85         cp = mp = NULL;
86        
87         while ((*string) && (*wild != _T('*')))
88         {
89                 if ((_toupper(*wild) != _toupper(*string)) && (*wild != _T('?')))
90                 {
91                         return FALSE;
92                 }
93                 wild++;
94                 string++;
95         }
96                
97         while (*string)
98         {
99                 if (*wild == _T('*'))
100                 {
101                         if (!*++wild)
102                         {
103                                 return TRUE;
104                         }
105                         mp = wild;
106                         cp = string+1;
107                 }
108                 else
109                 if ((_toupper(*wild) == _toupper(*string)) || (*wild == _T('?')))
110                 {
111                         wild++;
112                         string++;
113                 }
114                 else
115                 {
116                         wild = mp;
117                         string = cp++;
118                 }
119         }
120                
121         while (*wild == _T('*'))
122         {
123                 wild++;
124         }
125         return (!*wild)? TRUE : FALSE;
126 }
127
128 //////////////////////////////////////////////////////////////////////////
129 //
130 //CDirChangeNotification member functions:
131 //
132 CDirChangeNotification::CDirChangeNotification(CDelayedDirectoryChangeHandler * pDelayedHandler, DWORD dwPartialPathOffset)
133 :m_pDelayedHandler( pDelayedHandler )
134 ,m_szFileName1(NULL)
135 ,m_szFileName2(NULL)
136 ,m_dwError(0UL)
137 ,m_dwPartialPathOffset(dwPartialPathOffset)
138 {
139         ASSERT( pDelayedHandler );
140 }
141
142 CDirChangeNotification::~CDirChangeNotification()
143 {
144         if( m_szFileName1 ) free(m_szFileName1), m_szFileName1 = NULL;
145         if( m_szFileName2 ) free(m_szFileName2), m_szFileName2 = NULL;
146 }
147
148 void CDirChangeNotification::DispatchNotificationFunction()
149 {
150         ASSERT( m_pDelayedHandler );
151         if( m_pDelayedHandler )
152                 m_pDelayedHandler->DispatchNotificationFunction( this );
153 }
154
155 void CDirChangeNotification::PostOn_FileAdded(LPCTSTR szFileName)
156 {
157         ASSERT( szFileName );
158         m_eFunctionToDispatch   = eOn_FileAdded;
159         m_szFileName1                   = _tcsdup( szFileName) ;
160         //
161         // post the message so it'll be dispatch by another thread.
162         PostNotification();
163
164 }
165 void CDirChangeNotification::PostOn_FileRemoved(LPCTSTR szFileName)
166 {
167         ASSERT( szFileName );
168         m_eFunctionToDispatch   = eOn_FileRemoved;
169         m_szFileName1                   = _tcsdup( szFileName) ;
170         //
171         // post the message so it'll be dispatched by another thread.
172         PostNotification();
173        
174 }
175 void CDirChangeNotification::PostOn_FileNameChanged(LPCTSTR szOldName, LPCTSTR szNewName)
176 {
177         ASSERT( szOldName && szNewName );
178
179         m_eFunctionToDispatch   = eOn_FileNameChanged;
180         m_szFileName1                   = _tcsdup( szOldName) ;
181         m_szFileName2                   = _tcsdup( szNewName) ;
182         //
183         // post the message so it'll be dispatched by another thread.
184         PostNotification();
185        
186 }
187
188 void CDirChangeNotification::PostOn_FileModified(LPCTSTR szFileName)
189 {
190         ASSERT( szFileName );
191
192         m_eFunctionToDispatch   = eOn_FileModified;
193         m_szFileName1                   = _tcsdup( szFileName );
194         //
195         // post the message so it'll be dispatched by another thread.
196         PostNotification();
197 }
198
199 void CDirChangeNotification::PostOn_ReadDirectoryChangesError(DWORD dwError, LPCTSTR szDirectoryName)
200 {
201         ASSERT( szDirectoryName );
202
203         m_eFunctionToDispatch = eOn_ReadDirectoryChangesError;
204         m_dwError                         = dwError;
205         m_szFileName1             = _tcsdup(szDirectoryName);
206         //
207         // post the message so it'll be dispatched by the another thread.
208         PostNotification();
209        
210 }
211
212 void CDirChangeNotification::PostOn_WatchStarted(DWORD dwError, LPCTSTR szDirectoryName)
213 {
214         ASSERT( szDirectoryName );
215
216         m_eFunctionToDispatch = eOn_WatchStarted;
217         m_dwError                         =     dwError;
218         m_szFileName1             = _tcsdup(szDirectoryName);
219
220         PostNotification();
221 }
222
223 void CDirChangeNotification::PostOn_WatchStopped(LPCTSTR szDirectoryName)
224 {
225         ASSERT( szDirectoryName );
226
227         m_eFunctionToDispatch = eOn_WatchStopped;
228         m_szFileName1             = _tcsdup(szDirectoryName);
229
230         PostNotification();
231 }
232
233 void CDirChangeNotification::PostOn_FileUploaded(LPCTSTR szFileName)
234 {
235         ASSERT( szFileName );
236
237         m_eFunctionToDispatch   = eOn_FileUploaded;
238         m_szFileName1                   = _tcsdup( szFileName );
239        
240         PostNotification();
241 }
242
243 void CDirChangeNotification::PostNotification()
244 {
245         ASSERT( m_pDelayedHandler );
246         if( m_pDelayedHandler )
247                 m_pDelayedHandler->PostNotification( this );
248 }
249
250 static LRESULT CALLBACK DelayedNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
251 //
252 //      This is the wndproc for the notification window
253 //
254 //      it's here to dispatch the notifications to the client
255 //
256 {
257         if( message == UWM_DELAYED_DIRECTORY_NOTIFICATION )
258         {
259                 CDirChangeNotification * pNotification = reinterpret_cast<CDirChangeNotification*>(lParam);
260                 ASSERT(  pNotification );
261                 if( pNotification )
262                 {
263                         DWORD dwEx(0);
264                         __try{
265                                 pNotification->DispatchNotificationFunction();
266                         }
267                         __except(dwEx = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER){
268                                 //An exception was raised:
269                                 //
270                                 //      Likely cause: there was a problem creating the CDelayedDirectoryChangeHandler::m_hWatchStoppedDispatchedEvent object
271                                 //      and the change handler object was deleted before the notification could be dispatched to this function.
272                                 //
273                                 //  or perhaps, somebody's implementation of an overridden function caused an exception
274                                 TRACE(_T("Following exception occurred: %d -- File: %s Line: %d\n"), dwEx, _T(__FILE__), __LINE__);
275                         }
276                 }
277                
278                 return 0UL;
279         }
280         else
281                 return DefWindowProc(hWnd,message,wParam,lParam);
282 }
283
284 //////////////////////////////////////////////////////////////////////
285 // Construction/Destruction
286 //////////////////////////////////////////////////////////////////////
287 //
288 //CDelayedNotificationWindow static member vars:
289 //
290 long CDelayedNotificationWindow::s_nRefCnt = 0L;
291 HWND CDelayedNotificationWindow::s_hWnd = NULL;
292 BOOL CDelayedNotificationWindow::s_bRegisterWindow = FALSE;
293 //
294 //
295 long CDelayedNotificationWindow::AddRef()//creates window for first time if necessary
296 {
297         if( InterlockedIncrement(&s_nRefCnt) == 1
298                 ||      !::IsWindow( s_hWnd ) )
299         {
300                 TRACE(_T("CDelayedNotificationWindow -- Creating the notification window\n"));
301                 VERIFY( CreateNotificationWindow() );
302         }
303         return s_nRefCnt;
304 }
305
306 long CDelayedNotificationWindow::Release()//destroys window for last time if necessary
307 {
308         long nRefCnt = -1;
309         if( (nRefCnt = InterlockedDecrement(&s_nRefCnt)) == 0 )
310         {
311                 //no body else using the window so destroy it?
312                 TRACE(_T("CDelayedNotificationWindow -- Destroying the notification window\n"));
313                 DestroyWindow( s_hWnd );
314                 s_hWnd = NULL;
315         }
316         return nRefCnt;
317 }
318 BOOL CDelayedNotificationWindow::RegisterWindowClass(LPCTSTR szClassName)
319 //
320 //      registers our own window class to use as the hidden notification window.
321 //
322 {
323         WNDCLASS wc = {0};
324        
325         wc.style = 0;
326         wc.hInstance            = GetInstanceHandle();
327         wc.lpszClassName        = szClassName;
328         wc.hbrBackground        = (HBRUSH)GetStockObject( WHITE_BRUSH );
329         wc.lpfnWndProc          = DelayedNotificationWndProc;
330        
331         ATOM ant = RegisterClass( &wc );
332         if( ant == NULL )
333         {
334                 TRACE(_T("CDirChangeNotification::RegisterWindowClass - RegisterClass failed: %d\n"), GetLastError());
335         }
336         return (BOOL)(ant!= NULL);
337        
338 }
339
340 BOOL CDelayedNotificationWindow::CreateNotificationWindow()
341 //
342 //      Create the hidden notification windows.
343 //
344 {
345         TCHAR szClassName[] = _T("Delayed_Message_Sender");
346         if( !s_bRegisterWindow )
347                 s_bRegisterWindow = RegisterWindowClass(szClassName);
348         s_hWnd  = CreateWindowEx(0, szClassName, _T("DelayedWnd"),0,0,0,0,0, NULL, 0,
349                                                         GetInstanceHandle(), NULL);
350         if( s_hWnd == NULL )
351         {
352                 TRACE(_T("Unable to create notification window! GetLastError(): %d\n"), GetLastError());
353                 TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__);
354         }
355        
356         return (BOOL)(s_hWnd != NULL);
357 }
358 void CDelayedNotificationWindow::PostNotification(CDirChangeNotification * pNotification)
359 //
360 //      Posts a message to a window created in the main
361 //      thread.
362 //      The main thread catches this message, and dispatches it in
363 //      the context of the main thread.
364 //
365 {
366         ASSERT( pNotification );
367         ASSERT( s_hWnd );
368         ASSERT( ::IsWindow( s_hWnd ) );
369
370         PostMessage(s_hWnd,
371                                 UWM_DELAYED_DIRECTORY_NOTIFICATION,
372                                 0,
373                                 reinterpret_cast<LPARAM>( pNotification ));
374
375 //  if you don't want the notification delayed,
376 // 
377 //      if( false )
378 //      {
379 //              pNotification->DispatchNotificationFunction();
380 //      }
381 }
382
383 /////////////////////////////////////////////////////////
384 //      CDelayedNoticationThread
385 //
386 long    CDelayedNotificationThread::s_nRefCnt = 0L;
387 HANDLE  CDelayedNotificationThread::s_hThread = NULL;
388 DWORD   CDelayedNotificationThread::s_dwThreadID = 0UL;
389
390 void CDelayedNotificationThread::PostNotification(CDirChangeNotification * pNotification)
391 {
392         ASSERT( s_hThread != NULL );
393         ASSERT( s_dwThreadID != 0 );
394
395         if(
396                 !PostThreadMessage(s_dwThreadID,
397                                                    UWM_DELAYED_DIRECTORY_NOTIFICATION,
398                                                    0,
399                                                    reinterpret_cast<LPARAM>(pNotification))
400           )
401         {
402                 //Note, this can sometimes fail.
403                 //Will fail if: s_dwThreadID references a invalid thread id(the thread has died for example)
404                 // OR will fail if the thread doesn't have a message queue.
405                 //
406                 //      This was failing because the thread had not been fully started by the time PostThreadMessage had been called
407                 //
408                 //Note: if this fails, it creates a memory leak because
409                 //the CDirChangeNotification object that was allocated and posted
410                 //to the thread is actually never going to be dispatched and then deleted.... it's
411                 //hanging in limbo.....
412
413                 //
414                 //      The fix for this situation was to force the thread that starts
415                 //      this worker thread to wait until the worker thread was fully started before
416                 //      continueing.  accomplished w/ an event... also.. posting a message to itself before signalling the
417                 //  'spawning' thread that it was started ensured that there was a message pump
418                 //  associated w/ the worker thread by the time PostThreadMessage was called.
419                 TRACE(_T("PostThreadMessage() failed while posting to thread id: %d! GetLastError(): %d%s\n"), s_dwThreadID, GetLastError(), GetLastError() == ERROR_INVALID_THREAD_ID? _T("(ERROR_INVALID_THREAD_ID)") : _T(""));
420         }
421 }
422
423 bool CDelayedNotificationThread::StartThread()
424 {
425         TRACE(_T("CDelayedNotificationThread::StartThread()\n"));
426         ASSERT( s_hThread == NULL
427                 &&      s_dwThreadID == 0 );
428         s_hThread = (HANDLE)_beginthreadex(NULL,0,
429                                                                 ThreadFunc, this, 0, (UINT*) &s_dwThreadID);
430         if( s_hThread )
431                 WaitForThreadStartup();
432
433         return s_hThread == NULL ? false : true;
434
435 }
436
437 bool CDelayedNotificationThread::StopThread()
438 {
439         TRACE(_T("CDelayedNotificationThread::StopThread()\n"));
440         if( s_hThread != NULL
441         &&      s_dwThreadID != 0 )
442         {
443                 PostThreadMessage(s_dwThreadID, WM_QUIT, 0,0);
444
445                 WaitForSingleObject(s_hThread, INFINITE);
446                 CloseHandle(s_hThread);
447                 s_hThread        = NULL;
448                 s_dwThreadID = 0UL;
449                 return true;
450         }
451         return true;//already shutdown
452 }
453
454 UINT __stdcall CDelayedNotificationThread::ThreadFunc(LPVOID lpvThis)
455 {
456         //UNREFERENCED_PARAMETER( lpvThis );
457         //
458         //      Implements a simple message pump
459         //
460         CDelayedNotificationThread * pThis = reinterpret_cast<CDelayedNotificationThread*>(lpvThis);
461         ASSERT( pThis );
462
463         //
464         //      Insure that this thread has a message queue by the time another
465         //      thread gets control and tries to use PostThreadMessage
466         //      problems can happen if someone tries to use PostThreadMessage
467         //      in between the time pThis->SignalThreadStartup() is called,
468         //      and the first call to GetMessage();
469
470         ::PostMessage(NULL, WM_NULL, 0,0);//if this thread didn't have a message queue before this, it does now.
471
472
473         //
474         //
475         //      Signal that this thread has started so that StartThread can continue.
476         //
477         if( pThis ) pThis->SignalThreadStartup();
478
479         TRACE(_T("CDelayedNotificationThread::ThreadFunc() ThreadID: %d -- Starting\n"), GetCurrentThreadId());
480         MSG msg;
481         do{
482                 while( GetMessage(&msg, NULL, 0,0) )//note GetMessage() can return -1, but only if i give it a bad HWND.(HWND for another thread for example)..i'm not giving an HWND, so no problemo here.
483                 {
484                         if( msg.message == UWM_DELAYED_DIRECTORY_NOTIFICATION )
485                         {
486                                 CDirChangeNotification * pNotification =
487                                                                 reinterpret_cast<CDirChangeNotification *>( msg.lParam );
488                                 DWORD dwEx(0UL);
489
490                                 __try{
491                                 if( pNotification )
492                                         pNotification->DispatchNotificationFunction();
493                                 }
494                                 __except(dwEx = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER){
495                                 //An exception was raised:
496                                 //
497                                 //      Likely causes:
498                                 //              * There was a problem creating the CDelayedDirectoryChangeHandler::m_hWatchStoppedDispatchedEvent object
499                                 //                      and the change handler object was deleted before the notification could be dispatched to this function.
500                                 //
501                                 //              * Somebody's implementation of an overridden virtual function caused an exception
502                                 TRACE(_T("The following exception occurred: %d -- File: %s Line: %d\n"), dwEx, _T(__FILE__), __LINE__);
503                                 }
504                         }
505                         else
506                         if( msg.message == WM_QUIT )
507                         {
508                                 break;
509                         }
510                 }
511         }while( msg.message != WM_QUIT );
512         TRACE(_T("CDelayedNotificationThread::ThreadFunc() exiting. ThreadID: %d\n"), GetCurrentThreadId());
513         return 0;
514 }
515
516 long CDelayedNotificationThread::AddRef()
517 {
518         if( InterlockedIncrement(&s_nRefCnt) == 1 )
519         {
520                 VERIFY( StartThread() );
521         }
522         return s_nRefCnt;
523 }
524 long CDelayedNotificationThread::Release()
525 {
526         if( InterlockedDecrement(&s_nRefCnt) <= 0 )
527         {
528                 s_nRefCnt = 0;
529                 VERIFY( StopThread() );
530         }
531         return s_nRefCnt;
532 }
533
534 ///////////////////////////////////////////////////////
535 //static member data for CDelayedDirectoryChangeHandler
536 HINSTANCE CDelayedDirectoryChangeHandler::s_hShlwapi_dll = NULL;//for the PathMatchSpec() function
537 BOOL CDelayedDirectoryChangeHandler::s_bShlwapi_dllExists = TRUE;
538 long CDelayedDirectoryChangeHandler::s_nRefCnt_hShlwapi = 0L;
539 FUNC_PatternMatchSpec CDelayedDirectoryChangeHandler::s_fpPatternMatchSpec = wildcmp;//default
540 ///////////////////////////////////////////////////////
541 //construction destruction
542 CDelayedDirectoryChangeHandler::CDelayedDirectoryChangeHandler(CDirectoryChangeHandler * pRealHandler, bool bAppHasGUI, LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter, DWORD dwFilterFlags)
543 : m_pDelayNotifier( NULL )
544  ,m_pRealHandler( pRealHandler )
545  ,m_szIncludeFilter(NULL)
546  ,m_szExcludeFilter(NULL)
547  ,m_dwFilterFlags( dwFilterFlags )
548  ,m_dwPartialPathOffset( 0UL )
549  ,m_hWatchStoppedDispatchedEvent(NULL)
550  ,m_nNumIncludeFilterSpecs(0)
551  ,m_nNumExcludeFilterSpecs(0)
552 {
553
554
555         ASSERT( m_pRealHandler );
556
557         InitializePathMatchFunc( szIncludeFilter, szExcludeFilter );
558
559         //
560         // See that we're
561         //
562
563
564         m_hWatchStoppedDispatchedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);//AUTO-RESET, not initially signalled
565         ASSERT( m_hWatchStoppedDispatchedEvent );
566        
567         if( bAppHasGUI )
568         {
569                 //
570                 //      The value true was passed to the CDirectoryChangeWatcher constructor.
571                 //      It's assumed that your app has a gui, that is, it implements
572                 //      a message pump.  To delay the notification to another thread,
573                 //      we'll use a hidden notification window.
574                 //
575                 m_pDelayNotifier = new CDelayedNotificationWindow();
576         }
577         else
578         {
579                 // The value 'false' was passed to the CDirectoryChangeWatcher constructor.
580                 //
581                 // Your app has no message pump... use a class that implements one for you
582                 // in a worker thread.
583                 //
584                 // Notifications will be executed in this worker thread.
585                 //
586                 m_pDelayNotifier = new CDelayedNotificationThread();
587         }
588 }
589
590 CDelayedDirectoryChangeHandler::~CDelayedDirectoryChangeHandler()
591 {
592         if( m_pRealHandler )
593                 delete m_pRealHandler, m_pRealHandler = NULL;
594         if( m_pDelayNotifier )
595                 delete m_pDelayNotifier, m_pDelayNotifier = NULL;
596
597         if( m_hWatchStoppedDispatchedEvent )
598                 CloseHandle(m_hWatchStoppedDispatchedEvent), m_hWatchStoppedDispatchedEvent = NULL;
599
600         if( m_szIncludeFilter ){
601                 if( m_nNumIncludeFilterSpecs == 1 )
602                         free(m_szIncludeFilter);
603                 else
604                 {
605                         TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
606                         for(int i(0); i < m_nNumIncludeFilterSpecs; ++i)
607                         {
608                                 free( *ppTmp++ );
609                         }
610                         free( m_szIncludeFilter );
611                 }
612                 m_szIncludeFilter = NULL;
613                 m_nNumIncludeFilterSpecs;
614         }
615         if( m_szExcludeFilter ) {
616                 if( m_nNumExcludeFilterSpecs == 1 )
617                         free(m_szExcludeFilter);
618                 else{
619                         TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
620                         for(int i(0); i < m_nNumExcludeFilterSpecs; ++i)
621                         {
622                                 free( *ppTmp++ );
623                         }
624                         free( m_szExcludeFilter );
625                 }
626                 m_szExcludeFilter = NULL;
627                 m_nNumExcludeFilterSpecs = 0;
628         }
629
630         UninitializePathMatchFunc();
631 }
632
633 BOOL CDelayedDirectoryChangeHandler::_PathMatchSpec(LPCTSTR szPath, LPCTSTR szPattern)
634 {
635         if( s_fpPatternMatchSpec )
636         {
637                 return s_fpPatternMatchSpec(szPath, szPattern);
638         }
639         ASSERT( FALSE );
640         return TRUE;//everything matches.
641 }
642
643 BOOL CDelayedDirectoryChangeHandler::InitializePathMatchFunc(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
644 //
645 //
646 //      To support the Include and Exclude filters, the PathMatchSpec function is used.
647 //      PathMatchSpec is only supported on NT4.0 if IE 4.0 is installed.
648 //
649 //      for the case where this code is running on NT 4.0 w/out IE 4.0, we use
650 //      a different function: wildcmp ()
651 //
652 //
653 //      This function attempts to load shlwapi.dll dynamically and find the PathMatchSpec function.
654 //
655 //      if the function PathMatchSpec can't be found, the function pointer s_fpPathMatchSpec is set to wildcmp.
656 //
657 //
658 //      Note:  wildcmp doesn't support multiple file specs separated by a semi-colon
659 //      as PathMatchSpec does.... we'll support it by parsing them
660 //      when we want to test the filters, we'll call the pattern matching functions multiple times...
661 //
662 {
663
664         //
665         //      Copy the include/exclude filters if specified...
666         //
667         //
668         if( IsEmptyString(szIncludeFilter)
669         &&      IsEmptyString(szExcludeFilter) )
670         {
671                 return TRUE;//both the include && exclude filters aren't specified
672                                         //no need to initialize the pattern matching function....
673                                         //if filters are never used, then
674                                         //one less dll is loaded.
675         }
676
677 #ifdef _TESTING_WILDCMP_ONLY_
678         s_hShlwapi_dll = NULL;
679         s_bShlwapi_dllExists = FALSE;
680         return InitializePatterns(szIncludeFilter, szExcludeFilter);
681 #endif
682
683
684
685         if( s_hShlwapi_dll != NULL )
686         {
687                 ASSERT( s_fpPatternMatchSpec != NULL );
688                 InterlockedIncrement(&s_nRefCnt_hShlwapi);
689
690                 return InitializePatterns(szIncludeFilter, szExcludeFilter);
691         }
692         else{
693                 if( s_bShlwapi_dllExists == TRUE )//either the dll exists, or we haven't tried loading it yet...
694                 {
695                         //The pattern match function hasn't been initialized yet....
696                         //
697                 
698                         s_hShlwapi_dll = ::LoadLibrary(_T("Shlwapi.dll"));
699                         if( s_hShlwapi_dll == NULL )
700                         {
701                                 s_bShlwapi_dllExists = FALSE;//don't try loading this dll again.
702                                 s_fpPatternMatchSpec = wildcmp;//even though it's set buy default, and this code will only get here once, set it just for fun.
703
704                                 return InitializePatterns(szIncludeFilter, szExcludeFilter);
705                                
706                         }
707                         else
708                         {
709                                 //Shlwapi.dll was found....check it for PathMatchSpec()
710 #ifdef UNICODE
711                                 s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecW");
712 #else
713
714                                 s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecA");
715 #endif
716
717        
718                                 if( s_fpPatternMatchSpec != NULL )
719                                 {
720                                         //UsesRealPathMatchSpec() will now return true.
721                                         //we're on NT w/ IE 4.0 or greater...(or Win2k/XP)
722                                         InterlockedIncrement(&s_nRefCnt_hShlwapi);
723                                         return InitializePatterns(szIncludeFilter, szExcludeFilter);
724                                 }
725                                 else
726                                 {
727                                         //we found shlwapi.dll, but it didn't have PathMatchSpec()
728                                         ::FreeLibrary( s_hShlwapi_dll );
729                                         s_hShlwapi_dll = NULL;
730                                         s_bShlwapi_dllExists = FALSE;
731
732                                         //instead of using PathMatchSpec()
733                                         //we'll use wildcmp()
734                                         s_fpPatternMatchSpec = wildcmp;
735                                         //UsesRealPathMatchSpec() will now return false w/out asserting.
736
737                                         return InitializePatterns(szIncludeFilter, szExcludeFilter);
738                                 }
739                         }
740                        
741                 }
742         }
743         return (s_fpPatternMatchSpec != NULL);
744 }
745
746 BOOL CDelayedDirectoryChangeHandler::InitializePatterns(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
747 {
748         ASSERT( !IsEmptyString(szIncludeFilter)   //one of these must have something in it,
749                 ||  !IsEmptyString(szExcludeFilter) );//or else this function shouldn't be called.
750
751         if( s_hShlwapi_dll != NULL )
752         {
753                 //we're using Shlwapi.dll's PathMatchSpec function....
754                 //we're running on NT4.0 w/ IE 4.0 installed, or win2k/winXP(or greater)
755                 //
756                 //      Copy the include/exclude filters if specified...
757                 //
758                 //
759                 // we're using the real PathMatchSpec() function which
760                 //      supports multiple pattern specs...(separated by a semi-colon)
761                 //      so there's only one filter spec as far as my code is concerned.
762                 //
763                 if( !IsEmptyString(szIncludeFilter) )
764                 {
765                         m_szIncludeFilter = _tcsdup(szIncludeFilter);
766                         ASSERT( m_szIncludeFilter );
767                         m_nNumIncludeFilterSpecs = 1;
768                 }
769                 if( !IsEmptyString(szExcludeFilter) )
770                 {
771                         m_szExcludeFilter = _tcsdup(szExcludeFilter);   
772                         ASSERT( m_szExcludeFilter );
773                         m_nNumExcludeFilterSpecs = 1;
774                 }       
775         }
776         else
777         {
778                 //shlwapi.dll isn't on this machine.... can happen on NT4.0 w/ out IE 4.0 installed.
779                 ASSERT( s_bShlwapi_dllExists == FALSE );
780
781                 //
782                 //      we're using the function wildcmp() instead of PathMatchSpec..
783                 //
784                 //      this means that multiple pattern specs aren't supported...
785                 // in order to support them, we'll tokenize the string into multiple
786                 // pattern specs and test the string multiple times(once per pattern spec)
787                 // in order to support multiple pat