2025-04-27 07:49:33 -04:00

753 lines
18 KiB
C++

#include "stdafx.h"
#include "Monitor.h"
#include "Lock.h"
#include "MyDebug.h"
#include "pudebug.h"
#include <process.h>
#ifdef DBG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
static String FileToDir( const String& );
// function objects for comparisons
struct DirCompare
{
DirCompare( const String& strDir )
: m_strDir( strDir ) {}
bool operator()( CMonitorDirPtr& pDir )
{
return ( m_strDir == pDir->Dir() );
}
String m_strDir;
};
struct FileCompare
{
FileCompare( const String& strFile )
: m_strFile( strFile ) {}
bool operator()( const CMonitorFilePtr& pFile )
{
return ( m_strFile == pFile->FileName() );
}
String m_strFile;
};
struct RegKeyCompare
{
RegKeyCompare( HKEY hKey, const String strSubKey )
: m_hKey(hKey), m_strSubKey( strSubKey ) {}
bool operator()( const CMonitorRegKeyPtr& pRegKey )
{
bool rc = false;
if ( m_hKey == pRegKey->m_hBaseKey )
{
rc = ( m_strSubKey == pRegKey->m_strKey );
}
return rc;
}
const HKEY m_hKey;
const String m_strSubKey;
};
String
FileToDir(
const String& strFile )
{
String strDir;
String::size_type pos = strFile.find_last_of( _T('\\') );
if ( pos != String::npos )
{
}
else
{
pos = strFile.find_first_of( _T(':') );
if ( pos != String::npos )
{
pos++;
}
}
if ( pos != String::npos )
{
strDir = strFile.substr( 0, pos );
}
return strDir;
}
//---------------------------------------------------------------------------
// CMonitorFile
//---------------------------------------------------------------------------
CMonitorFile::CMonitorFile(
const String& strFile,
const CMonitorNotifyPtr& pNotify )
: m_strFile( strFile ),
m_pNotify( pNotify )
{
GetFileTime( m_ft );
}
CMonitorFile::~CMonitorFile()
{
}
const String&
CMonitorFile::FileName() const
{
return m_strFile;
}
bool
CMonitorFile::GetFileTime(
FILETIME& ft )
{
bool rc = false;
WIN32_FILE_ATTRIBUTE_DATA fileInfo;
if (GetFileAttributesEx(m_strFile.c_str(),
GetFileExInfoStandard,
(LPVOID)&fileInfo)) {
ft = fileInfo.ftLastWriteTime;
rc = true;
}
return rc;
}
bool
CMonitorFile::CheckNotify()
{
bool rc = false;
FILETIME ft;
if ( (GetFileTime( ft) == false) || (::CompareFileTime( &ft, &m_ft ) != 0) )
{
ATLTRACE( _T("File %s has changed...notifying\n"), m_strFile.c_str() );
if ( m_pNotify.IsValid() )
{
m_pNotify->Notify();
}
rc = true;
}
m_ft = ft;
return rc;
}
//---------------------------------------------------------------------------
// CMonitorDir
//---------------------------------------------------------------------------
CMonitorDir::CMonitorDir(
const String& strDir )
: m_strDir( strDir )
{
m_hNotification = ::FindFirstChangeNotification(
m_strDir.c_str(),
FALSE,
FILE_NOTIFY_CHANGE_LAST_WRITE );
}
CMonitorDir::~CMonitorDir()
{
m_files.clear();
::FindCloseChangeNotification( m_hNotification );
}
void
CMonitorDir::AddFile(
const String& strFile,
const CMonitorNotifyPtr& pNotify )
{
// ATLTRACE( _T("Monitoring file %s\n"), strFile.c_str() );
m_files.push_back( new CMonitorFile( strFile, pNotify ) );
}
void
CMonitorDir::RemoveFile(
const String& strFile )
{
TVector<CMonitorFilePtr>::iterator iter = find_if(
m_files.begin(),
m_files.end(),
FileCompare( strFile ) );
if ( iter != m_files.end() )
{
// ATLTRACE( _T("Stopped monitoring file %s\n"), strFile.c_str() );
m_files.erase( iter );
}
else
{
// ATLTRACE( _T("Not monitoring file %s\n"), strFile.c_str() );
}
}
void
CMonitorDir::Notify()
{
for ( UINT i = 0; i < m_files.size(); i++ )
{
m_files[i]->CheckNotify();
}
::FindNextChangeNotification( m_hNotification );
}
ULONG
CMonitorDir::NumFiles() const
{
return m_files.size();
}
HANDLE
CMonitorDir::NotificationHandle() const
{
return m_hNotification;
}
const String&
CMonitorDir::Dir() const
{
return m_strDir;
}
//---------------------------------------------------------------------------
// CMonitorRegKey
//---------------------------------------------------------------------------
CMonitorRegKey::CMonitorRegKey(
HKEY hBaseKey,
const String& strKey,
const CMonitorNotifyPtr& pNotify )
: m_hEvt(NULL),
m_hKey(NULL),
m_pNotify( pNotify ),
m_strKey( strKey ),
m_hBaseKey( hBaseKey )
{
LONG l = ::RegOpenKeyEx(
hBaseKey,
strKey.c_str(),
0,
KEY_NOTIFY,
&m_hKey );
if ( l == ERROR_SUCCESS )
{
m_hEvt = IIS_CREATE_EVENT(
"CMonitorRegKey::m_hEvt",
this,
TRUE,
FALSE
);
if ( m_hEvt != NULL )
{
#if 0 // not available in Win95
// ask for notification when the key changes
l = ::RegNotifyChangeKeyValue(
m_hKey,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
m_hEvt,
TRUE );
if ( l == ERROR_SUCCESS )
{
// okay
}
else
{
ATLTRACE( _T("Couldn't get reg key notification\n") );
}
#endif // if 0
}
else
{
ATLTRACE( _T("Couldn't create event\n") );
}
}
else
{
ATLTRACE( _T("Couldn't open subkey: %s\n"), strKey.c_str() );
}
}
CMonitorRegKey::~CMonitorRegKey()
{
::RegCloseKey( m_hKey );
::CloseHandle( m_hEvt );
}
void
CMonitorRegKey::Notify()
{
if ( m_pNotify.IsValid() )
{
m_pNotify->Notify();
}
::ResetEvent( m_hEvt );
#if 0 // not available in Win95
::RegNotifyChangeKeyValue(
m_hKey,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
m_hEvt,
TRUE );
#endif
}
HANDLE
CMonitorRegKey::NotificationHandle() const
{
return m_hEvt;
}
//---------------------------------------------------------------------------
// CMonitor
//---------------------------------------------------------------------------
#include <irtldbg.h>
CMonitor::CMonitor()
: m_hevtBreak( NULL ),
m_hevtShutdown( NULL ),
m_hThread( NULL ),
m_bRunning( false ),
m_bStopping( false )
#ifdef STRING_TRACE_LOG
, m_stl(100, 1000)
#endif
{
SET_CRITICAL_SECTION_SPIN_COUNT(&m_cs.m_sec, IIS_DEFAULT_CS_SPIN_COUNT);
STL_PRINTF("Created monitor, %p", this);
#ifdef STRING_TRACE_LOG
IrtlTrace("Monitor::m_stl = %p\n", &m_stl);
#endif
}
CMonitor::~CMonitor()
{
StopAllMonitoring();
if ( m_hevtBreak != NULL )
{
::CloseHandle( m_hevtBreak );
}
if ( m_hevtShutdown != NULL )
{
::CloseHandle( m_hevtShutdown );
}
if ( m_hThread != NULL )
{
::CloseHandle( m_hThread );
}
STL_PRINTF("Destroying monitor, %p", this);
}
void
CMonitor::MonitorFile(
LPCTSTR szFile,
const CMonitorNotifyPtr& pMonNotify )
{
CLock l(m_cs);
if (m_bStopping)
return;
STL_PRINTF("MonitorFile(%s), Run=%d, Stop=%d, Thread=%p",
szFile, (int) m_bRunning, (int) m_bStopping, m_hThread);
String strFile( szFile );
String strDir = FileToDir( strFile );
CMonitorDirPtr pDir;
TVector<CMonitorDirPtr>::iterator iter = find_if(
m_dirs.begin(),
m_dirs.end(),
DirCompare( strDir ) );
if ( iter == m_dirs.end() )
{
// ATLTRACE( _T("Request to monitor new directory: %s\n"), strDir.c_str() );
pDir = new CMonitorDir( strDir );
m_dirs.push_back( pDir );
}
else
{
pDir = (*iter);
}
if ( pDir.IsValid() )
{
pDir->AddFile( strFile, pMonNotify );
if ( !m_bRunning )
{
StartUp();
}
else
{
::SetEvent( m_hevtBreak );
}
}
}
void
CMonitor::StopMonitoringFile(
LPCTSTR szFile )
{
String strFile( szFile );
String strDir = FileToDir( strFile );
CLock l(m_cs);
if (m_bStopping)
return;
STL_PRINTF("StopMonitoringFile(%s), Run=%d, Stop=%d, Thread=%p",
szFile, (int) m_bRunning, (int) m_bStopping, m_hThread);
TVector<CMonitorDirPtr>::iterator iter = find_if(
m_dirs.begin(),
m_dirs.end(),
DirCompare( strDir ) );
if ( iter != m_dirs.end() )
{
if ( (*iter).IsValid() )
{
(*iter)->RemoveFile( strFile );
if ( (*iter)->NumFiles() == 0 )
{
// no more files to monitor in this directory, remove it
m_dirs.erase(iter);
::SetEvent( m_hevtBreak );
}
}
}
else
{
// ATLTRACE( _T("Not monitorying file %s\n"), szFile );
}
}
void
CMonitor::MonitorRegKey(
HKEY hBaseKey,
LPCTSTR szSubKey,
const CMonitorNotifyPtr& pNotify )
{
String strSubKey = szSubKey;
// ATLTRACE( _T( "Request to monitor new key: %s\n"), szSubKey );
CLock l(m_cs);
if (m_bStopping)
return;
if ( find_if(
m_regKeys.begin(),
m_regKeys.end(),
RegKeyCompare( hBaseKey, szSubKey ) )
== m_regKeys.end() )
{
// not already begin monitored, add a new one
CMonitorRegKeyPtr pRegKey = new CMonitorRegKey( hBaseKey, szSubKey, pNotify );
m_regKeys.push_back(pRegKey);
// either start the monitoring thread or, inform it of a new key to monitor
if ( !m_bRunning )
{
StartUp();
}
else
{
::SetEvent( m_hevtBreak );
}
}
}
void
CMonitor::StopMonitoringRegKey(
HKEY hBaseKey,
LPCTSTR szSubKey )
{
String strSubKey = szSubKey;
CLock l(m_cs);
if (m_bStopping)
return;
TVector<CMonitorRegKeyPtr>::iterator iter = find_if(
m_regKeys.begin(),
m_regKeys.end(),
RegKeyCompare( hBaseKey, szSubKey ) );
if ( iter != m_regKeys.end() )
{
// ATLTRACE( _T( "Stopping monitoring of key: %s\n"), szSubKey );
m_regKeys.erase( iter );
::SetEvent( m_hevtBreak );
}
else
{
// ATLTRACE( _T("Not monitoring key: %s\n"), szSubKey );
}
}
void
CMonitor::StopAllMonitoring()
{
m_cs.Lock();
STL_PRINTF("StopAllMonitoring, Run=%d, Stop=%d, Thread=%p",
(int) m_bRunning, (int) m_bStopping, m_hThread);
if ( m_bRunning )
{
// clear all types of nodes here
m_bStopping = true;
m_regKeys.clear();
m_dirs.clear();
m_cs.Unlock(); // must unlock or DoMonitoring will deadlock
::SetEvent( m_hevtShutdown );
::WaitForSingleObject( m_hThread, INFINITE );
m_cs.Lock();
::CloseHandle( m_hThread );
m_hThread = NULL;
m_bRunning = false;
m_bStopping = false;
}
m_cs.Unlock();
}
bool
CMonitor::StartUp()
{
CLock l(m_cs);
bool rc = false;
STL_PRINTF("Startup, Run=%d, Stop=%d, Thread=%p",
(int) m_bRunning, (int) m_bStopping, m_hThread);
if (m_bStopping)
return false;
// Have we already started the thread?
if (m_bRunning)
{
_ASSERT(m_hevtBreak != NULL);
_ASSERT(m_hevtShutdown != NULL);
_ASSERT(m_hThread != NULL);
// Notify the thread that something has changed
::SetEvent( m_hevtBreak );
return true;
}
_ASSERT(m_hThread == NULL);
if ( m_hevtBreak == NULL )
{
m_hevtBreak = IIS_CREATE_EVENT(
"CMonitor::m_hevtBreak",
this,
FALSE, // auto event
FALSE
);
}
if ( m_hevtShutdown == NULL )
{
m_hevtShutdown = IIS_CREATE_EVENT(
"CMonitor::m_hevtShutdown",
this,
FALSE, // auto event
FALSE
);
}
if ( m_hevtBreak != NULL )
{
unsigned int iThreadID;
#if DBG
if( m_hThread != NULL || m_bRunning)
{
DebugBreak();
}
#endif
m_hThread = (HANDLE)_beginthreadex(
NULL,
0,
ThreadFunc,
this,
0,
&iThreadID );
STL_PRINTF("Startup, Thread=%p, Break=%p, Shutdown=%p",
m_hThread, m_hevtBreak, m_hevtShutdown);
if ( m_hThread != NULL )
{
ATLTRACE( _T("Started monitor (%p) thread %p\n"),
this, m_hThread );
m_bRunning = true;
rc = true;
}
}
return rc;
}
DWORD
CMonitor::DoMonitoring()
{
HANDLE* phEvt = NULL;
TVector<CMonitorNodePtr> nodes;
while ( 1 )
{
DWORD dwTimeOut = INFINITE;
if ( phEvt == NULL )
{
CLock l(m_cs);
// build the complete list of monitored nodes
nodes.clear();
nodes.insert( nodes.end(), m_dirs.begin(), m_dirs.end() );
nodes.insert( nodes.end(), m_regKeys.begin(), m_regKeys.end() );
// insert other types of nodes to monitor here
// Lazily shut down if there are no nodes to monitor
if ( nodes.size() == 0 )
{
// Since thread creation and destruction is a fairly
// expensive operation, wait for 5 minutes before killing
// thread
dwTimeOut = 5 * 60 * 1000;
}
// now create the array of event handles
phEvt = new HANDLE[ nodes.size() + 2 ];
phEvt[ 0 ] = m_hevtBreak;
phEvt[ 1 ] = m_hevtShutdown;
for ( UINT i = 0; i < nodes.size(); i++ )
{
phEvt[i+2] = nodes[i]->NotificationHandle();
}
}
else
STL_PUTS("phEvt != NULL");
DWORD dw = ::WaitForMultipleObjects(
nodes.size() + 2,
phEvt,
FALSE, // any event will do
dwTimeOut );
if ( dw == WAIT_TIMEOUT)
{
STL_PUTS("WAIT_TIMEOUT");
if ( nodes.size() == 0 )
{
STL_PRINTF("Nothing to watch: Shutting down, Stopping=%d",
(int) m_bStopping);
ATLTRACE( _T("Nothing to watch... ")
_T("stopping monitoring (%p) thread %p\n"),
this, m_hThread);
m_bRunning = false;
::CloseHandle( m_hThread );
m_hThread = NULL;
return 0;
}
}
// Was one of the events in phEvt signalled?
C_ASSERT( WAIT_OBJECT_0 == 0 );
if ( dw < ( WAIT_OBJECT_0 + nodes.size() + 2 ) )
{
CLock l(m_cs);
if ( dw >= WAIT_OBJECT_0 + 2)
{
// a monitored item has changed
nodes[ dw - ( WAIT_OBJECT_0 + 2 ) ]->Notify();
STL_PRINTF("Notifying object %d", dw - (WAIT_OBJECT_0 + 2));
}
else
{
// m_hevtBreak or m_hevtShutdown were signalled. If
// m_hevtBreak, then there was a manual break, and a node
// was probably added or removed, so the vector of nodes
// needs to be regenerated
nodes.clear();
delete[] phEvt;
phEvt = NULL;
// m_hevtShutdown was signalled
if ( dw == WAIT_OBJECT_0 + 1)
{
_ASSERT(m_bStopping);
STL_PRINTF("Shutting down, Stopping=%d",
(int) m_bStopping);
ATLTRACE(_T("Shutting down monitoring (%p) thread %p\n"),
this, m_hThread);
m_bRunning = false;
// Must NOT CloseHandle(m_hThread) because
// StopAllMonitoring is waiting on it
return 0;
}
else
STL_PUTS("m_hevtBreak");
}
}
else if ( dw == WAIT_FAILED )
{
CLock l(m_cs);
// something's wrong, we'll just clean up and exit
DWORD err = ::GetLastError();
ATLTRACE( _T("CMonitor: WaitForMultipleObjects error: 0x%x\n"),
err );
ATLTRACE( _T( "CMonitor: abandoning wait thread\n") );
nodes.clear();
delete[] phEvt;
phEvt = NULL;
m_dirs.clear();
m_regKeys.clear();
m_bRunning = false;
::CloseHandle( m_hThread );
m_hThread = NULL;
return err;
}
} // end infinite while
}
unsigned
__stdcall
CMonitor::ThreadFunc(
void* pv)
{
CMonitor* pMon = (CMonitor*) pv;
DWORD rc = -1;
try
{
if ( pMon )
{
rc = pMon->DoMonitoring();
}
}
catch( ... )
{
}
_endthreadex(rc);
return rc;
}