📄 directorychanges.cpp
字号:
//
// 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...
m_pCurrentRecord = pOld;
}
return (BOOL)(m_pCurrentRecord != pOld);
}
return FALSE;
}
BOOL CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord)
/*****************************************
Copies the FILE_NOTIFY_INFORMATION record to the beginning of the buffer
specified in the constructor.
The size of the current record is returned in DWORD & dwSizeOfCurrentRecord.
*****************************************/
{
ASSERT( m_pBuffer && m_pCurrentRecord );
if( !m_pCurrentRecord ) return FALSE;
BOOL bRetVal = TRUE;
//determine the size of the current record.
ref_dwSizeOfCurrentRecord = sizeof( FILE_NOTIFY_INFORMATION );
//subtract out sizeof FILE_NOTIFY_INFORMATION::FileName[1]
WCHAR FileName[1];//same as is defined for FILE_NOTIFY_INFORMATION::FileName
UNREFERENCED_PARAMETER(FileName);
ref_dwSizeOfCurrentRecord -= sizeof(FileName);
//and replace it w/ value of FILE_NOTIFY_INFORMATION::FileNameLength
ref_dwSizeOfCurrentRecord += m_pCurrentRecord->FileNameLength;
ASSERT( (DWORD)((LPBYTE)m_pCurrentRecord + ref_dwSizeOfCurrentRecord) <= m_dwBufferSize );
ASSERT( (void*)m_pBuffer != (void*)m_pCurrentRecord );//if this is the case, your buffer is way too small
if( (void*)m_pBuffer != (void*) m_pCurrentRecord )
{//copy the m_pCurrentRecord to the beginning of m_pBuffer
ASSERT( (DWORD)m_pCurrentRecord > (DWORD)m_pBuffer + ref_dwSizeOfCurrentRecord);//will it overlap?
__try{
memcpy(m_pBuffer, m_pCurrentRecord, ref_dwSizeOfCurrentRecord);
bRetVal = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
TRACE(_T("EXCEPTION! CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer() -- probably because bytes overlapped in a call to memcpy()"));
bRetVal = FALSE;
}
}
//else
//there was only one record in this buffer, and m_pCurrentRecord is already at the beginning of the buffer
return bRetVal;
}
DWORD CFileNotifyInformation::GetAction() const
{
ASSERT( m_pCurrentRecord );
if( m_pCurrentRecord )
return m_pCurrentRecord->Action;
return 0UL;
}
CString CFileNotifyInformation::GetFileName() const
{
//
//BUG FIX:
// File Name's longer than 130 characters are truncated
//
// Thanks Edric @ uo_edric@hotmail.com for pointing this out.
if( m_pCurrentRecord )
{
WCHAR wcFileName[ MAX_PATH + 1] = {0};//L"";
memcpy( wcFileName,
m_pCurrentRecord->FileName,
//min( MAX_PATH, m_pCurrentRecord->FileNameLength) <-- buggy line
min( (MAX_PATH * sizeof(WCHAR)), m_pCurrentRecord->FileNameLength));
return CString( wcFileName );
}
return CString();
}
static inline bool HasTrailingBackslash(const CString & str )
{
if( str.GetLength() > 0
&& str[ str.GetLength() - 1 ] == _T('\\') )
return true;
return false;
}
CString CFileNotifyInformation::GetFileNameWithPath(const CString & strRootPath) const
{
CString strFileName( strRootPath );
//if( strFileName.Right(1) != _T("\\") )
if( !HasTrailingBackslash( strRootPath ) )
strFileName += _T("\\");
strFileName += GetFileName();
return strFileName;
}
/////////////////////////////////////////////////////////////////////////////////
class CPrivilegeEnabler
//
// Enables privileges for this process
// first time CDirectoryChangeWatcher::WatchDirectory() is called.
//
// It's a singleton.
//
{
private:
CPrivilegeEnabler();//ctor
public:
~CPrivilegeEnabler(){};
static CPrivilegeEnabler & Instance();
//friend static CPrivilegeEnabler & Instance();
};
CPrivilegeEnabler::CPrivilegeEnabler()
{
LPCTSTR arPrivelegeNames[] = {
SE_BACKUP_NAME, // these two are required for the FILE_FLAG_BACKUP_SEMANTICS flag used in the call to
SE_RESTORE_NAME,// CreateFile() to open the directory handle for ReadDirectoryChangesW
SE_CHANGE_NOTIFY_NAME //just to make sure...it's on by default for all users.
//<others here as needed>
};
for(int i = 0; i < sizeof(arPrivelegeNames) / sizeof(arPrivelegeNames[0]); ++i)
{
if( !EnablePrivilege(arPrivelegeNames[i], TRUE) )
{
TRACE(_T("Unable to enable privilege: %s -- GetLastError(): %d\n"), arPrivelegeNames[i], GetLastError());
TRACE(_T("CDirectoryChangeWatcher notifications may not work as intended due to insufficient access rights/process privileges.\n"));
TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__);
}
}
}
CPrivilegeEnabler & CPrivilegeEnabler::Instance()
{
static CPrivilegeEnabler theInstance;//constructs this first time it's called.
return theInstance;
}
//
//
//
///////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDirectoryChangeHandler::CDirectoryChangeHandler()
: m_nRefCnt( 1 ),
m_pDirChangeWatcher( NULL ),
m_nWatcherRefCnt( 0L )
{
}
CDirectoryChangeHandler::~CDirectoryChangeHandler()
{
UnwatchDirectory();
}
long CDirectoryChangeHandler::AddRef()
{
return InterlockedIncrement(&m_nRefCnt);
}
long CDirectoryChangeHandler::Release()
{
long nRefCnt = -1;
if( (nRefCnt = InterlockedDecrement(&m_nRefCnt)) == 0 )
delete this;
return nRefCnt;
}
long CDirectoryChangeHandler::CurRefCnt()const
{
return m_nRefCnt;
}
BOOL CDirectoryChangeHandler::UnwatchDirectory()
{
CSingleLock lock(&m_csWatcher, TRUE);
ASSERT( lock.IsLocked() );
if( m_pDirChangeWatcher )
return m_pDirChangeWatcher->UnwatchDirectory( this );
return TRUE;
}
long CDirectoryChangeHandler::ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher)
{
ASSERT( pDirChangeWatcher );
CSingleLock lock(&m_csWatcher, TRUE);
if( m_pDirChangeWatcher
&& m_pDirChangeWatcher != pDirChangeWatcher )
{
TRACE(_T("CDirectoryChangeHandler...is becoming used by a different CDirectoryChangeWatcher!\n"));
TRACE(_T("Directories being handled by this object will now be unwatched.\nThis object is now being used to ")
_T("handle changes to a directory watched by different CDirectoryChangeWatcher object, probably on a different directory"));
if( UnwatchDirectory() )
{
m_pDirChangeWatcher = pDirChangeWatcher;
m_nWatcherRefCnt = 1; //when this reaches 0, set m_pDirChangeWatcher to NULL
return m_nWatcherRefCnt;
}
else
{
ASSERT( FALSE );//shouldn't get here!
}
}
else
{
ASSERT( !m_pDirChangeWatcher || m_pDirChangeWatcher == pDirChangeWatcher );
m_pDirChangeWatcher = pDirChangeWatcher;
if( m_pDirChangeWatcher )
return InterlockedIncrement(&m_nWatcherRefCnt);
}
return m_nWatcherRefCnt;
}
long CDirectoryChangeHandler::ReleaseReferenceToWatcher(CDirectoryChangeWatcher * pDirChangeWatcher)
{
ASSERT( m_pDirChangeWatcher == pDirChangeWatcher );
CSingleLock lock(&m_csWatcher, TRUE);
long nRef;
if( (nRef = InterlockedDecrement(&m_nWatcherRefCnt)) <= 0L )
{
m_pDirChangeWatcher = NULL; //Setting this to NULL so that this->UnwatchDirectory() which is called in the dtor
//won't call m_pDirChangeWatcher->UnwatchDirecotry(this).
//m_pDirChangeWatcher may point to a destructed object depending on how
//these classes are being used.
m_nWatcherRefCnt = 0L;
}
return nRef;
}
//
//
// Default implmentations for CDirectoryChangeHandler's virtual functions.
//
//
void CDirectoryChangeHandler::On_FileAdded(const CString & strFileName)
{
TRACE(_T("The following file was added: %s\n"), strFileName);
}
void CDirectoryChangeHandler::On_FileRemoved(const CString & strFileName)
{
TRACE(_T("The following file was removed: %s\n"), strFileName);
}
void CDirectoryChangeHandler::On_FileModified(const CString & strFileName)
{
TRACE(_T("The following file was modified: %s\n"), strFileName);
}
void CDirectoryChangeHandler::On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
{
TRACE(_T("The file %s was RENAMED to %s\n"), strOldFileName, strNewFileName);
}
void CDirectoryChangeHandler::On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName)
{
TRACE(_T("WARNING!!!!!\n") );
TRACE(_T("An error has occurred on a watched directory!\n"));
TRACE(_T("This directory has become unwatched! -- %s \n"), strDirectoryName);
TRACE(_T("ReadDirectoryChangesW has failed! %d"), dwError);
ASSERT( FALSE );//you should override this function no matter what. an error will occur someday.
}
void CDirectoryChangeHandler::On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
{
if( dwError == 0 )
TRACE(_T("A watch has begun on the following directory: %s\n"), strDirectoryName);
else
TRACE(_T("A watch failed to start on the following directory: (Error: %d) %s\n"),dwError, strDirectoryName);
}
void CDirectoryChangeHandler::On_WatchStopped(const CString & strDirectoryName)
{
TRACE(_T("The watch on the following directory has stopped: %s\n"), strDirectoryName);
}
bool CDirectoryChangeHandler::On_FilterNotification(DWORD /*dwNotifyAction*/, LPCTSTR /*szFileName*/, LPCTSTR /*szNewFileName*/)
//
// bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName);
//
// This function gives your class a chance to filter unwanted notifications.
//
// PARAMETERS:
// DWORD dwNotifyAction -- specifies the event to filter
// LPCTSTR szFileName -- specifies the name of the file for the event.
// LPCTSTR szNewFileName -- specifies the new file name of a file that has been renamed.
//
// RETURN VALUE:
// return true from this function, and you will receive the notification.
// return false from this function, and your class will NOT receive the notification.
//
// Valid values of dwNotifyAction:
// FILE_ACTION_ADDED -- On_FileAdded() is about to be called.
// FILE_ACTION_REMOVED -- On_FileRemoved() is about to be called.
// FILE_ACTION_MODIFIED -- On_FileModified() is about to be called.
// FILE_ACTION_RENAMED_OLD_NAME-- On_FileNameChanged() is about to be call.
//
//
// NOTE: When the value of dwNotifyAction is FILE_ACTION_RENAMED_OLD_NAME,
// szFileName will be the old name of the file, and szNewFileName will
// be the new name of the renamed file.
//
// The default implementation always returns true, indicating that all notifications will
// be sent.
//
{
return true;
}
void CDirectoryChangeHandler::SetChangedDirectoryName(const CString & strChangedDirName)
{
m_strChangedDirectoryName = strChangedDirName;
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
CDirectoryChangeWatcher::CDirectoryChangeWatcher(bool bAppHasGUI /*= true*/, DWORD dwFilterFlags/*=FILTERS_CHECK_FILE_NAME_ONLY*/)
: m_hCompPort( NULL )
,m_hThread( NULL )
,m_dwThreadID( 0UL )
,m_bAppHasGUI( bAppHasGUI )
,m_dwFilterFlags( dwFilterFlags == 0? FILTERS_DEFAULT_BEHAVIOR : dwFilterFlags)
{
//NOTE:
// The bAppHasGUI variable indicates that you have a message pump associated
// with the main thread(or the thread that first calls CDirectoryChangeWatcher::WatchDirectory() ).
// Directory change notifications are dispatched to your main thread.
//
// If your app doesn't have a gui, then pass false. Doing so causes a worker thread
// to be created that implements a message pump where it dispatches/executes the notifications.
// It's ok to pass false even if your app does have a GUI.
// Passing false is required for Console applications, or applications without a message pump.
// Note that notifications are fired in a worker thread.
//
//NOTE:
//
//
}
CDirectoryChangeWatcher::~CDirectoryChangeWatcher()
{
UnwatchAllDirectories();
if( m_hCompPort )
{
CloseHandle( m_hCompPort );
m_hCompPort = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -