📄 delayeddirectorychangehandler.cpp
字号:
}
void CDelayedDirectoryChangeHandler::PostNotification(CDirChangeNotification * pNotification)
{
if( m_pDelayNotifier )
m_pDelayNotifier->PostNotification( pNotification );
}
inline bool IsNonFilterableEvent( CDirChangeNotification::eFunctionToDispatch eEvent)
// Helper function
// For filtering events..... these functions can not be filtered out.
//
{
if( eEvent == CDirChangeNotification::eOn_WatchStarted
|| eEvent == CDirChangeNotification::eOn_WatchStopped
|| eEvent == CDirChangeNotification::eOn_ReadDirectoryChangesError )
{
return true;
}
else
return false;
}
DWORD GetPathOffsetBasedOnFilterFlags(CDirChangeNotification * pNot, DWORD dwFilterFlags)
{
ASSERT( pNot && dwFilterFlags != 0 );
//helps support the filter options FILTERS_CHECK_FULL_PATH, FILTERS_CHECK_PARTIAL_PATH, and FILTERS_CHECK_FILE_NAME_ONLY
DWORD dwFileNameOffset = 0;//offset needed to support FILTERS_CHECK_FULL_PATH
if( dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_FILE_NAME_ONLY )
{
//set the offset to support FILTERS_CHECK_FILE_NAME_ONLY
TCHAR * pSlash = _tcsrchr(pNot->m_szFileName1, _T('\\'));
if( pSlash )
dwFileNameOffset = (++pSlash - pNot->m_szFileName1);
//
// Because file name change notifications take place in the same directory,
// the same dwFileNameOffset can be used for the szNewFileName(pNot->m_szFileName2)
// when checking the filter against the new file name.
//
}
else
if( dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH)
{
//
// partial path offset is the offset
// from the beginning of the file name,
// to the end of the watched directory path...
// ie: If you're watching "C:\Temp"
// and the file C:\Temp\SubFolder\FileName.txt" is changed,
// the partial path offset will give you "SubFolder\FileName.txt"
// when this is checked against the include/exclude filter.
//
dwFileNameOffset = pNot->m_dwPartialPathOffset;
}
//else
//if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_FULL_PATH )
// dwFileNameOffset = 0;
return dwFileNameOffset;
}
bool CDelayedDirectoryChangeHandler::NotifyClientOfFileChange(CDirChangeNotification * pNot)
//
//
// Perform the tests to see if the client wants to be notified of this
// file change notification.
//
// Tests performed:
//
// Event test: Not all events can be filtered out.
// On_ReadDirectoryChangesError
// cannot be filtered out.
// Filter flags test: User can specify flags so that no tests are performed....all notifications are sent to the user.
//
// Filter test: Test the notification file name against include and exclude filters.
//
// Only files changes matching the INCLUDE filter will be passed to the client.
// By not specifying an include filter, all file changes are passed to the client.
//
// Any files matching the EXCLUDE filter will not be passed to the client.
//
//
// Note: For the file name change event:
// If the old file name does not pass the tests for the include and exclude filter
// but the NEW file name does pass the test for the filters, then the client IS notified.
//
// Client test: The CDirectoryChangeHandler derived class is given a chance to filter the event by calling
// CDirectoryChangeHandler::On_FilterNotification()
//
// RETURN VALUE:
// If this function returns true, the notification function is called.
// If it returns false, the notification is ignored.
// The client is notified by calling CDirectoryChangeHandler's virtual functions On_FileAdded(),On_FileRemoved(), etc.
{
ASSERT( pNot );
ASSERT( m_pRealHandler );
//
// Some events can't be ignored, or filtered out.
//
if( ((m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_ANY_FILTER_TESTS) == CDirectoryChangeWatcher::FILTERS_DONT_USE_ANY_FILTER_TESTS)
|| IsNonFilterableEvent( pNot->m_eFunctionToDispatch ) )
{
// Either this is a non-filterable event, or we're not using any filters...
// client is notified of all events..
return true;
}
//
// See if user wants to test CDirectoryChangeHandler::On_FilterNotification()
// before tests are performed against the file name, and filter specifications
//
if( (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_TEST_HANDLER_FIRST )//specified that CDirectoryChangeHandler::On_FilterNotification is to be called before any other filter tests
&& !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_HANDLER_FILTER)//and did not specify that this CDirectoryChangeHandler::On_FilterNotification is not to be called..
&& !m_pRealHandler->On_FilterNotification(pNot->m_eFunctionToDispatch, pNot->m_szFileName1, pNot->m_szFileName2) )
{
//
// Client specified to test handler first, and it didn't pass the test... don't notify the client.
//
return false;
}
//else
//
// this file change passed the user test, continue testing
// to see if it passes the filter tests.
DWORD dwFileNameOffset = GetPathOffsetBasedOnFilterFlags(pNot, m_dwFilterFlags);
//
// See if the changed file matches the include or exclude filter
// Only allow notifications for included files
// that have not been exluded.
//
if(!(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS) )
{
if( false == IncludeThisNotification(pNot->m_szFileName1 + dwFileNameOffset)
|| true == ExcludeThisNotification(pNot->m_szFileName1 + dwFileNameOffset) )
{
if( pNot->m_eFunctionToDispatch != CDirChangeNotification::eOn_FileNameChanged )
return false;
else{
//Special case for file name change:
//
// the old file name didn't pass the include/exclude filter
// but if the new name passes the include/exclude filter,
// we will pass it on to the client...
if( false == IncludeThisNotification(pNot->m_szFileName2 + dwFileNameOffset)
|| true == ExcludeThisNotification(pNot->m_szFileName2 + dwFileNameOffset) )
{
// the new file name didn't pass the include/exclude filter test either
// so don't pass the notification on...
return false;
}
}
}
}
//
// Finally, let the client determine whether or not it wants this file notification
// if this test has not already been performed...
//
if( (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_TEST_HANDLER_FIRST)
|| (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_HANDLER_FILTER) )
{
// if we got this far, and this flag was specified,
// it's already passed this test,
// or we're not checking it based on the filter flag FILTERS_DONT_USE_HANDLER_FILTER....
return true;
}
else
{
if( m_pRealHandler->On_FilterNotification(pNot->m_eFunctionToDispatch,
pNot->m_szFileName1,
pNot->m_szFileName2) )
{
return true;
}
else
{
//else client's derived CDirectoryChangeHandler class chose
// not to be notified of this file change
return false;
}
}
}
bool CDelayedDirectoryChangeHandler::IncludeThisNotification(LPCTSTR szFileName)
//
// The Include filter specifies which file names we should allow to notifications
// for... otherwise these notifications are not dispatched to the client's code.
//
// Tests the file name to see if it matches a filter specification
//
// RETURN VALUES:
//
// true : notifications for this file are to be included...
// notifiy the client by calling the appropriate CDirectoryChangeHandler::On_Filexxx() function.
// false: this file is not included.... do not notifiy the client...
//
{
ASSERT( szFileName );
if( m_szIncludeFilter == NULL ) // no filter specified, all files pass....
return true;
if( m_nNumIncludeFilterSpecs == 1 )
{
return _PathMatchSpec(szFileName, m_szIncludeFilter)? true : false;
}
else
{
TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
for(int i(0); i < m_nNumIncludeFilterSpecs; ++i )
{
if( _PathMatchSpec(szFileName, *ppTmp++) )
return true;
}
return false;
}
return false;
}
bool CDelayedDirectoryChangeHandler::ExcludeThisNotification(LPCTSTR szFileName)
//
// Tests the file name to see if it matches a filter specification
// if this function returns true, it means that this notification
// is NOT to be passed to the client.... changes to this kind of file
// are not
//
// RETURN VALUES:
//
// true : notifications for this file are to be filtered out(EXCLUDED)...
// do not notifify the client code.
// false: notifications for this file are NOT to be filtered out
//
//
{
ASSERT( szFileName );
if( m_szExcludeFilter == NULL ) // no exclude filter... nothing is excluded...
return false;
if( m_nNumExcludeFilterSpecs == 1 )
{
if( _PathMatchSpec(szFileName, m_szExcludeFilter) )
return true;//exclude this notification...
return false;
}
else
{
TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
for(int i(0); i < m_nNumExcludeFilterSpecs; ++i )
{
if( _PathMatchSpec(szFileName, *ppTmp++) )
return true;//exclude this one...
}
return false;//didn't match any exclude filters...don't exclude it
}
/**
if( m_szExcludeFilter == NULL //no exclude filter specified, not excluding anything....
|| !PathMatchSpec(szFileName, m_szExcludeFilter) )//or didn't match filter pattern.. this is not excluded...
{
return false;
}
return true;
***/
}
void CDelayedDirectoryChangeHandler::DispatchNotificationFunction(CDirChangeNotification * pNotification)
/*****************************************************
This function is called when we want the notification to execute.
******************************************************/
{
ASSERT( m_pRealHandler );
ASSERT( pNotification );
if( pNotification && m_pRealHandler )
{
//
// Allow the client to ignore the notification
//
//
if( NotifyClientOfFileChange(pNotification))
{
switch( pNotification->m_eFunctionToDispatch )
{
case CDirChangeNotification::eOn_FileAdded:
m_pRealHandler->On_FileAdded( pNotification->m_szFileName1 );
break;
case CDirChangeNotification::eOn_FileRemoved:
m_pRealHandler->On_FileRemoved( pNotification->m_szFileName1 );
break;
case CDirChangeNotification::eOn_FileNameChanged:
m_pRealHandler->On_FileNameChanged( pNotification->m_szFileName1, pNotification->m_szFileName2 );
break;
case CDirChangeNotification::eOn_FileModified:
m_pRealHandler->On_FileModified( pNotification->m_szFileName1 );
break;
case CDirChangeNotification::eOn_ReadDirectoryChangesError:
m_pRealHandler->On_ReadDirectoryChangesError( pNotification->m_dwError, pNotification->m_szFileName1 );
break;
case CDirChangeNotification::eOn_WatchStarted:
m_pRealHandler->On_WatchStarted(pNotification->m_dwError, pNotification->m_szFileName1);
break;
case CDirChangeNotification::eOn_WatchStopped:
try{
//
// The exception handler is just in case of the condition described in DirectoryChanges.h
// in the comments for On_WatchStopped()
//
m_pRealHandler->On_WatchStopped(pNotification->m_szFileName1);
}catch(...){
MessageBeep( 0xffff );
MessageBeep( 0xffff );
#ifdef DEBUG
MessageBox(NULL,_T("An RTFM Exception was raised in On_WatchStopped() -- see Comments for CDirectoryChangeHandler::On_WatchStopped() in DirectoryChanges.h."), _T("Programmer Note(DEBUG INFO):"), MB_ICONEXCLAMATION | MB_OK);
#endif
}
//
// Signal that the On_WatchStopped() function has been dispatched.
//
if( m_hWatchStoppedDispatchedEvent )
SetEvent(m_hWatchStoppedDispatchedEvent);
break;
case CDirChangeNotification::eFunctionNotDefined:
default:
break;
}//end switch()
}
}
if( pNotification ) //
DisposeOfNotification(pNotification);// deletes or releases the notification object from memory/use
//
}
BOOL CDelayedDirectoryChangeHandler::WaitForOnWatchStoppedDispatched( )
//
// When shutting down, m_pRealHandler->On_WatchStopped() will be called.
// Because it's possible that this object will be deleted before the notification
// can be dispatched to the other thread, we have to wait until we know that it's been executed
// before returning control.
//
// This function signals that the function has been dispatched to the other
// thread and it will be safe to delete this object once this has returned.
//
{
ASSERT( m_hWatchStoppedDispatchedEvent );
DWORD dwWait = WAIT_FAILED;
if( m_hWatchStoppedDispatchedEvent )
{
if( m_bAppHasGUI == false )
{
//
// The function will be dispatched to another thread...
// just wait for the event to be signalled....
do{
dwWait = WaitForSingleObject(m_hWatchStoppedDispatchedEvent, 5000);//wait five seconds
if( dwWait != WAIT_OBJECT_0 )
{
TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
}
}while( dwWait != WAIT_OBJECT_0 );
}
else
{
//
// Note to self: This thread doesn't have a message Q, and therefore can't attach to
// receive messages and process them... MsgWaitForMultipleObjects won't wake up for messages
// unless i attach myself the the other threads input Q....
// just use MsgWaitForMultipleObjects() in place of WaitForSingleObject in the places where it's used...
//
do{
dwWait = MsgWaitForMultipleObjects(1, &m_hWatchStoppedDispatchedEvent,
FALSE, 5000,
QS_ALLEVENTS);//wake up for all events, sent messages, posted messages etc.
switch(dwWait)
{
case WAIT_OBJECT_0:
{
//
// The event has become signalled
//
}break;
case WAIT_OBJECT_0 + 1:
{
//
// There is a message in this thread's queue, so
// MsgWaitForMultipleObjects returned.
// Process those messages, and wait again.
MSG msg;
while( PeekMessage(&msg, NULL, 0,0, PM_REMOVE ) )
{
if( msg.message != WM_QUIT)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
/****
NOTE: putting WM_QUIT back in the Q caused problems. forget about it.
****/
break;
}
}
}break;
case WAIT_TIMEOUT:
{
TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
}break;
}
}while( dwWait != WAIT_OBJECT_0 );
ASSERT( dwWait == WAIT_OBJECT_0 );
}
}
else
{
TRACE(_T("WARNING: Unable to wait for notification that the On_WatchStopped function has been dispatched to another thread.\n"));
TRACE(_T("An Exception may occur shortly.\n"));
TRACE(_T("File: %s Line: %d"), _T( __FILE__ ), __LINE__);
}
return (dwWait == WAIT_OBJECT_0 );
}
void CDelayedDirectoryChangeHandler::SetChangedDirectoryName(const CString & strChangedDirName)
{
ASSERT( m_pRealHandler );
CDirectoryChangeHandler::SetChangedDirectoryName(strChangedDirName);
if( m_pRealHandler )
m_pRealHandler->SetChangedDirectoryName( strChangedDirName );
}
const CString & CDelayedDirectoryChangeHandler::GetChangedDirectoryName() const
{
if( m_pRealHandler )
return m_pRealHandler->GetChangedDirectoryName();
return CDirectoryChangeHandler::GetChangedDirectoryName();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -