📄 delayeddirectorychangehandler.cpp
字号:
if( InterlockedIncrement(&s_nRefCnt) == 1 )
{
VERIFY( StartThread() );
}
return s_nRefCnt;
}
long CDelayedNotificationThread::Release()
{
if( InterlockedDecrement(&s_nRefCnt) <= 0 )
{
s_nRefCnt = 0;
VERIFY( StopThread() );
}
return s_nRefCnt;
}
///////////////////////////////////////////////////////
//static member data for CDelayedDirectoryChangeHandler
HINSTANCE CDelayedDirectoryChangeHandler::s_hShlwapi_dll = NULL;//for the PathMatchSpec() function
BOOL CDelayedDirectoryChangeHandler::s_bShlwapi_dllExists = TRUE;
long CDelayedDirectoryChangeHandler::s_nRefCnt_hShlwapi = 0L;
FUNC_PatternMatchSpec CDelayedDirectoryChangeHandler::s_fpPatternMatchSpec = wildcmp;//default
///////////////////////////////////////////////////////
//construction destruction
CDelayedDirectoryChangeHandler::CDelayedDirectoryChangeHandler(CDirectoryChangeHandler * pRealHandler, bool bAppHasGUI, LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter, DWORD dwFilterFlags)
: m_pDelayNotifier( NULL )
,m_pRealHandler( pRealHandler )
,m_szIncludeFilter(NULL)
,m_szExcludeFilter(NULL)
,m_dwFilterFlags( dwFilterFlags )
,m_dwPartialPathOffset( 0UL )
,m_hWatchStoppedDispatchedEvent(NULL)
,m_nNumIncludeFilterSpecs(0)
,m_nNumExcludeFilterSpecs(0)
{
ASSERT( m_pRealHandler );
InitializePathMatchFunc( szIncludeFilter, szExcludeFilter );
//
// See that we're
//
m_hWatchStoppedDispatchedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);//AUTO-RESET, not initially signalled
ASSERT( m_hWatchStoppedDispatchedEvent );
if( bAppHasGUI )
{
//
// The value true was passed to the CDirectoryChangeWatcher constructor.
// It's assumed that your app has a gui, that is, it implements
// a message pump. To delay the notification to another thread,
// we'll use a hidden notification window.
//
m_pDelayNotifier = new CDelayedNotificationWindow();
}
else
{
// The value 'false' was passed to the CDirectoryChangeWatcher constructor.
//
// Your app has no message pump... use a class that implements one for you
// in a worker thread.
//
// Notifications will be executed in this worker thread.
//
m_pDelayNotifier = new CDelayedNotificationThread();
}
}
CDelayedDirectoryChangeHandler::~CDelayedDirectoryChangeHandler()
{
if( m_pRealHandler )
delete m_pRealHandler, m_pRealHandler = NULL;
if( m_pDelayNotifier )
delete m_pDelayNotifier, m_pDelayNotifier = NULL;
if( m_hWatchStoppedDispatchedEvent )
CloseHandle(m_hWatchStoppedDispatchedEvent), m_hWatchStoppedDispatchedEvent = NULL;
if( m_szIncludeFilter ){
if( m_nNumIncludeFilterSpecs == 1 )
free(m_szIncludeFilter);
else
{
TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
for(int i(0); i < m_nNumIncludeFilterSpecs; ++i)
{
free( *ppTmp++ );
}
free( m_szIncludeFilter );
}
m_szIncludeFilter = NULL;
m_nNumIncludeFilterSpecs;
}
if( m_szExcludeFilter ) {
if( m_nNumExcludeFilterSpecs == 1 )
free(m_szExcludeFilter);
else{
TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
for(int i(0); i < m_nNumExcludeFilterSpecs; ++i)
{
free( *ppTmp++ );
}
free( m_szExcludeFilter );
}
m_szExcludeFilter = NULL;
m_nNumExcludeFilterSpecs = 0;
}
UninitializePathMatchFunc();
}
BOOL CDelayedDirectoryChangeHandler::_PathMatchSpec(LPCTSTR szPath, LPCTSTR szPattern)
{
if( s_fpPatternMatchSpec )
{
return s_fpPatternMatchSpec(szPath, szPattern);
}
ASSERT( FALSE );
return TRUE;//everything matches.
}
BOOL CDelayedDirectoryChangeHandler::InitializePathMatchFunc(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
//
//
// To support the Include and Exclude filters, the PathMatchSpec function is used.
// PathMatchSpec is only supported on NT4.0 if IE 4.0 is installed.
//
// for the case where this code is running on NT 4.0 w/out IE 4.0, we use
// a different function: wildcmp ()
//
//
// This function attempts to load shlwapi.dll dynamically and find the PathMatchSpec function.
//
// if the function PathMatchSpec can't be found, the function pointer s_fpPathMatchSpec is set to wildcmp.
//
//
// Note: wildcmp doesn't support multiple file specs separated by a semi-colon
// as PathMatchSpec does.... we'll support it by parsing them
// when we want to test the filters, we'll call the pattern matching functions multiple times...
//
{
//
// Copy the include/exclude filters if specified...
//
//
if( IsEmptyString(szIncludeFilter)
&& IsEmptyString(szExcludeFilter) )
{
return TRUE;//both the include && exclude filters aren't specified
//no need to initialize the pattern matching function....
//if filters are never used, then
//one less dll is loaded.
}
#ifdef _TESTING_WILDCMP_ONLY_
s_hShlwapi_dll = NULL;
s_bShlwapi_dllExists = FALSE;
return InitializePatterns(szIncludeFilter, szExcludeFilter);
#endif
if( s_hShlwapi_dll != NULL )
{
ASSERT( s_fpPatternMatchSpec != NULL );
InterlockedIncrement(&s_nRefCnt_hShlwapi);
return InitializePatterns(szIncludeFilter, szExcludeFilter);
}
else{
if( s_bShlwapi_dllExists == TRUE )//either the dll exists, or we haven't tried loading it yet...
{
//The pattern match function hasn't been initialized yet....
//
s_hShlwapi_dll = ::LoadLibrary(_T("Shlwapi.dll"));
if( s_hShlwapi_dll == NULL )
{
s_bShlwapi_dllExists = FALSE;//don't try loading this dll again.
s_fpPatternMatchSpec = wildcmp;//even though it's set buy default, and this code will only get here once, set it just for fun.
return InitializePatterns(szIncludeFilter, szExcludeFilter);
}
else
{
//Shlwapi.dll was found....check it for PathMatchSpec()
#ifdef UNICODE
s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecW");
#else
s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecA");
#endif
if( s_fpPatternMatchSpec != NULL )
{
//UsesRealPathMatchSpec() will now return true.
//we're on NT w/ IE 4.0 or greater...(or Win2k/XP)
InterlockedIncrement(&s_nRefCnt_hShlwapi);
return InitializePatterns(szIncludeFilter, szExcludeFilter);
}
else
{
//we found shlwapi.dll, but it didn't have PathMatchSpec()
::FreeLibrary( s_hShlwapi_dll );
s_hShlwapi_dll = NULL;
s_bShlwapi_dllExists = FALSE;
//instead of using PathMatchSpec()
//we'll use wildcmp()
s_fpPatternMatchSpec = wildcmp;
//UsesRealPathMatchSpec() will now return false w/out asserting.
return InitializePatterns(szIncludeFilter, szExcludeFilter);
}
}
}
}
return (s_fpPatternMatchSpec != NULL);
}
BOOL CDelayedDirectoryChangeHandler::InitializePatterns(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
{
ASSERT( !IsEmptyString(szIncludeFilter) //one of these must have something in it,
|| !IsEmptyString(szExcludeFilter) );//or else this function shouldn't be called.
if( s_hShlwapi_dll != NULL )
{
//we're using Shlwapi.dll's PathMatchSpec function....
//we're running on NT4.0 w/ IE 4.0 installed, or win2k/winXP(or greater)
//
// Copy the include/exclude filters if specified...
//
//
// we're using the real PathMatchSpec() function which
// supports multiple pattern specs...(separated by a semi-colon)
// so there's only one filter spec as far as my code is concerned.
//
if( !IsEmptyString(szIncludeFilter) )
{
m_szIncludeFilter = _tcsdup(szIncludeFilter);
ASSERT( m_szIncludeFilter );
m_nNumIncludeFilterSpecs = 1;
}
if( !IsEmptyString(szExcludeFilter) )
{
m_szExcludeFilter = _tcsdup(szExcludeFilter);
ASSERT( m_szExcludeFilter );
m_nNumExcludeFilterSpecs = 1;
}
}
else
{
//shlwapi.dll isn't on this machine.... can happen on NT4.0 w/ out IE 4.0 installed.
ASSERT( s_bShlwapi_dllExists == FALSE );
//
// we're using the function wildcmp() instead of PathMatchSpec..
//
// this means that multiple pattern specs aren't supported...
// in order to support them, we'll tokenize the string into multiple
// pattern specs and test the string multiple times(once per pattern spec)
// in order to support multiple patterns.
//
//
// m_szIncludeFilter & m_szExclude filter will be used like TCHAR**'s instead of TCHAR*'s
//
m_nNumIncludeFilterSpecs = 0;
if( !IsEmptyString(szIncludeFilter) )
{
TCHAR * szTmpFilter = _tcsdup(szIncludeFilter);
TCHAR * pTok = _tcstok( szTmpFilter, _T(";"));
while( pTok )
{
m_nNumIncludeFilterSpecs++;
pTok = _tcstok(NULL, _T(";"));
}
if( m_nNumIncludeFilterSpecs == 1 )
m_szIncludeFilter = _tcsdup(szIncludeFilter);
else
{ //allocate room for pointers .. one for each token...
m_szIncludeFilter = (TCHAR*)malloc( m_nNumIncludeFilterSpecs * sizeof(TCHAR*));
free(szTmpFilter);
szTmpFilter = _tcsdup(szIncludeFilter);
pTok = _tcstok(szTmpFilter, _T(";"));
TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
while(pTok)
{
*ppTmp = _tcsdup(pTok);
ppTmp++;
pTok = _tcstok(NULL, _T(";"));
}
}
free(szTmpFilter);
}
//
// Do the same for the Exclude filter...
//
m_nNumExcludeFilterSpecs = 0;
if( !IsEmptyString(szExcludeFilter) )
{
TCHAR * szTmpFilter = _tcsdup(szExcludeFilter);
TCHAR * pTok = _tcstok( szTmpFilter, _T(";"));
while( pTok )
{
m_nNumExcludeFilterSpecs++;
pTok = _tcstok(NULL, _T(";"));
}
if( m_nNumExcludeFilterSpecs == 1 )
m_szExcludeFilter = _tcsdup(szExcludeFilter);
else
{ //allocate room for pointers .. one for each token...
m_szExcludeFilter = (TCHAR*)malloc( m_nNumExcludeFilterSpecs * sizeof(TCHAR*));
free(szTmpFilter);
szTmpFilter = _tcsdup(szExcludeFilter);
pTok = _tcstok(szTmpFilter, _T(";"));
TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
while(pTok)
{
*ppTmp = _tcsdup(pTok);
ppTmp++;
pTok = _tcstok(NULL, _T(";"));
}
}
free(szTmpFilter);
}
}
return (m_szExcludeFilter!= NULL || (m_szIncludeFilter != NULL));
}
void CDelayedDirectoryChangeHandler::UninitializePathMatchFunc()
{
if( s_bShlwapi_dllExists == TRUE
&& s_hShlwapi_dll != NULL )
{
if( InterlockedDecrement(&s_nRefCnt_hShlwapi) <= 0)
{
s_nRefCnt_hShlwapi = 0;
FreeLibrary( s_hShlwapi_dll );
s_hShlwapi_dll = NULL;
s_fpPatternMatchSpec = wildcmp;
}
}
}
bool CDelayedDirectoryChangeHandler::UsesRealPathMatchSpec() const
//are we using PathMatchSpec() or wildcmp()?
{
if( s_hShlwapi_dll != NULL && s_fpPatternMatchSpec != NULL )
return true;
if( s_hShlwapi_dll == NULL && s_fpPatternMatchSpec != NULL )
return false;
ASSERT( FALSE );//this function was called before InitializePathMatchFunc()
//oops!
return false;
}
static inline bool HasTrailingBackslash(const CString & str )
{
if( str.GetLength() > 0
&& str[ str.GetLength() - 1 ] == _T('\\') )
return true;
return false;
}
void CDelayedDirectoryChangeHandler::SetPartialPathOffset(const CString & strWatchedDirName)
{
if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH )
{
//set the offset to
if( HasTrailingBackslash( strWatchedDirName ) )
m_dwPartialPathOffset = strWatchedDirName.GetLength();
else
m_dwPartialPathOffset = strWatchedDirName.GetLength() + 1;
}
else
m_dwPartialPathOffset = 0;
}
CDirChangeNotification * CDelayedDirectoryChangeHandler::GetNotificationObject()
//
// Maybe in future I'll keep a pool of these
// objects around to increase performance...
// using objects from a cache will be faster
// than allocated and destroying a new one each time.
//
//
{
ASSERT( m_pRealHandler );
return new CDirChangeNotification(this, m_dwPartialPathOffset);//helps support FILTERS_CHECK_PARTIAL_PATH
}
void CDelayedDirectoryChangeHandler::DisposeOfNotification(CDirChangeNotification * pNotification)
{
delete pNotification;
}
//These functions are called when the directory to watch has had a change made to it
void CDelayedDirectoryChangeHandler::On_FileAdded(const CString & strFileName)
{
CDirChangeNotification * p = GetNotificationObject();
ASSERT( p );
if( p ) p->PostOn_FileAdded( strFileName );
}
void CDelayedDirectoryChangeHandler::On_FileRemoved(const CString & strFileName)
{
CDirChangeNotification * p = GetNotificationObject();
ASSERT( p );
if( p ) p->PostOn_FileRemoved( strFileName );
}
void CDelayedDirectoryChangeHandler::On_FileModified(const CString & strFileName)
{
CDirChangeNotification * p = GetNotificationObject();
ASSERT( p );
if( p ) p->PostOn_FileModified( strFileName );
}
void CDelayedDirectoryChangeHandler::On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
{
CDirChangeNotification * p = GetNotificationObject();
ASSERT( p );
if( p ) p->PostOn_FileNameChanged( strOldFileName, strNewFileName );
}
void CDelayedDirectoryChangeHandler::On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName)
{
CDirChangeNotification * p = GetNotificationObject();
ASSERT( p );
if( p ) p->PostOn_ReadDirectoryChangesError( dwError, strDirectoryName );
}
void CDelayedDirectoryChangeHandler::On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
{
if( !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_NO_WATCHSTART_NOTIFICATION))
{
CDirChangeNotification * p = GetNotificationObject();
if( p ) p->PostOn_WatchStarted(dwError, strDirectoryName);
}
}
void CDelayedDirectoryChangeHandler::On_WatchStopped(const CString & strDirectoryName)
{
if( !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_NO_WATCHSTOP_NOTIFICATION))
{
CDirChangeNotification * p = GetNotificationObject();
if( p ){
if( m_hWatchStoppedDispatchedEvent )
::ResetEvent(m_hWatchStoppedDispatchedEvent);
p->PostOn_WatchStopped( strDirectoryName );
// Wait that this function has been dispatched to the other thread
// before continueing. This object may be getting deleted
// soon after this function returns, and before the function can be
// dispatched to the other thread....
WaitForOnWatchStoppedDispatched();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -