789 lines
22 KiB
C++
789 lines
22 KiB
C++
|
//
|
||
|
// MODULE: DirMonitor.cpp
|
||
|
//
|
||
|
// PURPOSE: Monitor changes to LST, DSC, HTI, BES files.
|
||
|
//
|
||
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
|
||
|
//
|
||
|
// AUTHOR: Joe Mabel
|
||
|
//
|
||
|
// ORIGINAL DATE: 9-17-98
|
||
|
//
|
||
|
// NOTES:
|
||
|
//
|
||
|
// Version Date By Comments
|
||
|
//--------------------------------------------------------------------
|
||
|
// V3.0 09-17-98 JM
|
||
|
//
|
||
|
|
||
|
#pragma warning(disable:4786)
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include <algorithm>
|
||
|
#include "DirMonitor.h"
|
||
|
#include "event.h"
|
||
|
#include "apiwraps.h"
|
||
|
#include "CharConv.h"
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
#include "LocalLSTReader.h"
|
||
|
#include "CHMFileReader.h"
|
||
|
#endif
|
||
|
#include "apgts.h" // Need for Local-Online macros.
|
||
|
|
||
|
const DWORD k_secsDefaultReloadDelay = 40; // In practice, this default should not matter,
|
||
|
// because SetReloadDelay() should be called before
|
||
|
// SetResourceDirectory(). However, 40 is a typical
|
||
|
// reasonable value for m_secsReloadDelay.
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CTopicFileTracker
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CTopicFileTracker::CTopicFileTracker() :
|
||
|
CFileTracker()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CTopicFileTracker::~CTopicFileTracker()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CTopicFileTracker::AddTopicInfo(const CTopicInfo & topicinfo)
|
||
|
{
|
||
|
m_topicinfo = topicinfo;
|
||
|
|
||
|
// set CFileTracker member variables accordingly for files that are present.
|
||
|
// If they are not present i.e. empty strings then adding them here results in
|
||
|
// unnecessary event log entries.
|
||
|
AddFile(topicinfo.GetDscFilePath());
|
||
|
|
||
|
CString strHTI = topicinfo.GetHtiFilePath();
|
||
|
if (!strHTI.IsEmpty())
|
||
|
AddFile(strHTI);
|
||
|
|
||
|
CString strBES = topicinfo.GetBesFilePath();
|
||
|
if (!strBES.IsEmpty())
|
||
|
AddFile(strBES);
|
||
|
}
|
||
|
|
||
|
const CTopicInfo & CTopicFileTracker::GetTopicInfo() const
|
||
|
{
|
||
|
return m_topicinfo;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CTemplateFileTracker
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CTemplateFileTracker::CTemplateFileTracker() :
|
||
|
CFileTracker()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CTemplateFileTracker::~CTemplateFileTracker()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CTemplateFileTracker::AddTemplateName( const CString& strTemplateName )
|
||
|
{
|
||
|
m_strTemplateName= strTemplateName;
|
||
|
AddFile( strTemplateName );
|
||
|
}
|
||
|
|
||
|
const CString& CTemplateFileTracker::GetTemplateName() const
|
||
|
{
|
||
|
return m_strTemplateName;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// CDirectoryMonitor::ThreadStatus
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
/* static */ CString CDirectoryMonitor::ThreadStatusText(ThreadStatus ts)
|
||
|
{
|
||
|
switch(ts)
|
||
|
{
|
||
|
case eBeforeInit: return _T("Before Init");
|
||
|
case eFail: return _T("Fail");
|
||
|
case eWaitDirPath: return _T("Wait For Dir Path");
|
||
|
case eWaitChange: return _T("Wait for Change");
|
||
|
case eWaitSettle: return _T("Wait to Settle");
|
||
|
case eRun: return _T("Run");
|
||
|
case eBeforeWaitChange: return _T("Before Wait Change");
|
||
|
case eExiting: return _T("Exiting");
|
||
|
default: return _T("");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
// CDirectoryMonitor
|
||
|
// This class does the bulk of its work on a separate thread.
|
||
|
// The thread is created in the constructor by starting static function
|
||
|
// CDirectoryMonitor::DirectoryMonitorTask
|
||
|
// That function, in turn does its work by calling private members of this class that
|
||
|
// are specific to use on the DirectoryMonitorTask thread.
|
||
|
// When this goes out of scope, its own destructor calls ShutDown to stop the thread,
|
||
|
// waits for the thread to shut.
|
||
|
// The following methods are available for other threads communicating with that thread:
|
||
|
// CDirectoryMonitor::SetReloadDelay
|
||
|
// CDirectoryMonitor::SetResourceDirectory
|
||
|
/////////////////////////////////////////////////////////////////////
|
||
|
CDirectoryMonitor::CDirectoryMonitor(CTopicShop & TopicShop, const CString& strTopicName) :
|
||
|
m_strTopicName(strTopicName),
|
||
|
m_TopicShop(TopicShop),
|
||
|
m_pErrorTemplate(NULL),
|
||
|
m_strDirPath(_T("")), // Essential that this starts blank. Getting a different
|
||
|
// value is how we start the DirectoryMonitorTask thread.
|
||
|
m_bDirPathChanged(false),
|
||
|
m_bShuttingDown(false),
|
||
|
m_secsReloadDelay(k_secsDefaultReloadDelay),
|
||
|
m_pTrackLst( NULL ),
|
||
|
m_pTrackErrorTemplate( NULL ),
|
||
|
m_pLst( NULL ),
|
||
|
m_dwErr(0),
|
||
|
m_ThreadStatus(eBeforeInit),
|
||
|
m_time(0)
|
||
|
{
|
||
|
enum {eHevMon, eHevShut, eThread, eOK} Progress = eHevMon;
|
||
|
SetThreadStatus(eBeforeInit);
|
||
|
|
||
|
m_hevMonitorRequested = ::CreateEvent(
|
||
|
NULL,
|
||
|
FALSE, // release one thread (the DirectoryMonitorTask) on signal
|
||
|
FALSE, // initially non-signalled
|
||
|
NULL);
|
||
|
if (m_hevMonitorRequested)
|
||
|
{
|
||
|
Progress = eHevShut;
|
||
|
m_hevThreadIsShut = ::CreateEvent(
|
||
|
NULL,
|
||
|
FALSE, // release one thread (this one) on signal
|
||
|
FALSE, // initially non-signalled
|
||
|
NULL);
|
||
|
|
||
|
if (m_hevThreadIsShut)
|
||
|
{
|
||
|
Progress = eThread;
|
||
|
DWORD dwThreadID; // No need to hold onto dwThreadID in member variable.
|
||
|
// All Win32 functions take the handle m_hThread instead.
|
||
|
// The one reason you'd ever want to know this ID is for
|
||
|
// debugging
|
||
|
|
||
|
// Note that there is no corresponding ::CloseHandle(m_hThread).
|
||
|
// That is because the thread goes out of existence on the implicit
|
||
|
// ::ExitThread() when DirectoryMonitorTask returns. See documentation of
|
||
|
// ::CreateThread for further details JM 10/22/98
|
||
|
m_hThread = ::CreateThread( NULL,
|
||
|
0,
|
||
|
(LPTHREAD_START_ROUTINE)DirectoryMonitorTask,
|
||
|
this,
|
||
|
0,
|
||
|
&dwThreadID);
|
||
|
|
||
|
if (m_hThread)
|
||
|
Progress = eOK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Progress != eOK)
|
||
|
{
|
||
|
m_dwErr = GetLastError();
|
||
|
CString str;
|
||
|
str.Format(_T("%d"), m_dwErr);
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
(Progress == eHevMon) ? _T("Can't create monitor event")
|
||
|
: (Progress == eHevShut) ? _T("Can't create \"shut\" event")
|
||
|
: _T("Can't create thread"),
|
||
|
str,
|
||
|
EV_GTS_ERROR_DIRMONITORTHREAD );
|
||
|
SetThreadStatus(eFail);
|
||
|
|
||
|
if (m_hevMonitorRequested)
|
||
|
::CloseHandle(m_hevMonitorRequested);
|
||
|
|
||
|
if (m_hevThreadIsShut)
|
||
|
::CloseHandle(m_hevThreadIsShut);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CDirectoryMonitor::~CDirectoryMonitor()
|
||
|
{
|
||
|
ShutDown();
|
||
|
|
||
|
if (m_hevMonitorRequested)
|
||
|
::CloseHandle(m_hevMonitorRequested);
|
||
|
|
||
|
if (m_hevThreadIsShut)
|
||
|
::CloseHandle(m_hevThreadIsShut);
|
||
|
|
||
|
if (m_pErrorTemplate)
|
||
|
delete m_pErrorTemplate;
|
||
|
|
||
|
if (m_pTrackLst)
|
||
|
delete m_pTrackLst;
|
||
|
|
||
|
if (m_pTrackErrorTemplate)
|
||
|
delete m_pTrackErrorTemplate;
|
||
|
}
|
||
|
|
||
|
void CDirectoryMonitor::SetThreadStatus(ThreadStatus ts)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
m_ThreadStatus = ts;
|
||
|
time(&m_time);
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
DWORD CDirectoryMonitor::GetStatus(ThreadStatus &ts, DWORD & seconds) const
|
||
|
{
|
||
|
time_t timeNow;
|
||
|
LOCKOBJECT();
|
||
|
ts = m_ThreadStatus;
|
||
|
time(&timeNow);
|
||
|
seconds = timeNow - m_time;
|
||
|
UNLOCKOBJECT();
|
||
|
return m_dwErr;
|
||
|
}
|
||
|
|
||
|
// Only for use by this class's own destructor.
|
||
|
void CDirectoryMonitor::ShutDown()
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
m_bShuttingDown = true;
|
||
|
if (m_hThread)
|
||
|
{
|
||
|
::SetEvent(m_hevMonitorRequested);
|
||
|
UNLOCKOBJECT();
|
||
|
|
||
|
// Wait for a set period, if failure then log error msg and wait infinite.
|
||
|
WAIT_INFINITE( m_hevThreadIsShut );
|
||
|
}
|
||
|
else
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// For use by the DirectoryMonitorTask thread.
|
||
|
// Read LST file and add any topics that are not already in previously read LST file contents
|
||
|
void CDirectoryMonitor::LstFileDrivesTopics()
|
||
|
{
|
||
|
// previous LST file contents, saved for comparison.
|
||
|
CAPGTSLSTReader *pLstOld = m_pLst;
|
||
|
|
||
|
if (! m_strLstPath.IsEmpty() )
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
m_pLst = new CLocalLSTReader( CPhysicalFileReader::makeReader( m_strLstPath ), m_strTopicName);
|
||
|
#else
|
||
|
m_pLst = new CAPGTSLSTReader( dynamic_cast<CPhysicalFileReader*>(new CNormalFileReader(m_strLstPath)) );
|
||
|
#endif
|
||
|
}
|
||
|
catch (bad_alloc&)
|
||
|
{
|
||
|
// Restore old LST contents.
|
||
|
m_pLst = pLstOld;
|
||
|
|
||
|
// Rethrow exception, logging handled upstream.
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
if (! m_pLst->Read())
|
||
|
{
|
||
|
// Restore old LST contents and log error.
|
||
|
delete m_pLst;
|
||
|
m_pLst = pLstOld;
|
||
|
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
_T(""),
|
||
|
_T(""),
|
||
|
EV_GTS_ERROR_LST_FILE_READ );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CTopicInfoVector arrNewTopicInfo;
|
||
|
m_pLst->GetDifference(pLstOld, arrNewTopicInfo);
|
||
|
if (pLstOld)
|
||
|
delete pLstOld;
|
||
|
|
||
|
for (CTopicInfoVector::iterator itNewTopicInfo = arrNewTopicInfo.begin();
|
||
|
itNewTopicInfo != arrNewTopicInfo.end();
|
||
|
itNewTopicInfo++
|
||
|
)
|
||
|
{
|
||
|
// Let the Topic Shop know about the new topic
|
||
|
m_TopicShop.AddTopic(*itNewTopicInfo);
|
||
|
|
||
|
// add it to our list of files to track for changes
|
||
|
CTopicFileTracker TopicFileTracker;
|
||
|
TopicFileTracker.AddTopicInfo(*itNewTopicInfo);
|
||
|
LOCKOBJECT();
|
||
|
try
|
||
|
{
|
||
|
m_arrTrackTopic.push_back(TopicFileTracker);
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
CString str;
|
||
|
// Note STL exception in event log.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
_T(""),
|
||
|
EV_GTS_STL_EXCEPTION );
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// if topic shop not already open, open it
|
||
|
m_TopicShop.OpenShop();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Called by the topic shop to add alternate templates to track.
|
||
|
void CDirectoryMonitor::AddTemplateToTrack( const CString& strTemplateName )
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
try
|
||
|
{
|
||
|
CTemplateFileTracker TemplateFileTracker;
|
||
|
TemplateFileTracker.AddTemplateName( strTemplateName );
|
||
|
|
||
|
m_arrTrackTemplate.push_back( TemplateFileTracker );
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
CString str;
|
||
|
// Note STL exception in event log.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
_T(""),
|
||
|
EV_GTS_STL_EXCEPTION );
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
|
||
|
// For use by the DirectoryMonitorTask thread.
|
||
|
void CDirectoryMonitor::ReadErrorTemplate()
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
|
||
|
if (m_pErrorTemplate)
|
||
|
delete m_pErrorTemplate;
|
||
|
|
||
|
CString str = k_strDefaultErrorTemplateBefore;
|
||
|
str += k_strErrorTemplateKey;
|
||
|
str += k_strDefaultErrorTemplateAfter;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
m_pErrorTemplate = new CSimpleTemplate( CPhysicalFileReader::makeReader( m_strErrorTemplatePath ), str );
|
||
|
}
|
||
|
catch (bad_alloc&)
|
||
|
{
|
||
|
UNLOCKOBJECT();
|
||
|
|
||
|
// Rethrow the exception.
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
m_pErrorTemplate->Read();
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// For use by any thread. In this class because CDirectoryMonitor needs to own
|
||
|
// ErrorTemplate, since it can change during run of system.
|
||
|
void CDirectoryMonitor::CreateErrorPage(const CString & strError, CString& out) const
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
|
||
|
if (m_pErrorTemplate)
|
||
|
{
|
||
|
vector<CTemplateInfo> arrTemplateInfo;
|
||
|
CTemplateInfo info(k_strErrorTemplateKey, strError);
|
||
|
try
|
||
|
{
|
||
|
arrTemplateInfo.push_back(info);
|
||
|
m_pErrorTemplate->CreatePage( arrTemplateInfo, out );
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
CString str;
|
||
|
// Note STL exception in event log.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
_T(""),
|
||
|
EV_GTS_STL_EXCEPTION );
|
||
|
|
||
|
// Generate the default error page to be safe.
|
||
|
out = k_strDefaultErrorTemplateBefore + strError + k_strDefaultErrorTemplateAfter;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
out = k_strDefaultErrorTemplateBefore + strError + k_strDefaultErrorTemplateAfter;
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// Must be called on DirectoryMonitorTask thread.
|
||
|
// Handles all work of monitoring the directory. Loops till shutdown.
|
||
|
void CDirectoryMonitor::Monitor()
|
||
|
{
|
||
|
enum {
|
||
|
#ifndef LOCAL_TROUBLESHOOTER
|
||
|
eDirChange, // file in directory changed
|
||
|
#endif
|
||
|
eHev, // shutdown or change what directory
|
||
|
eNumHandles };
|
||
|
|
||
|
// array of handles we use when waiting for multiple events.
|
||
|
// Initialize first entry to default bad value.
|
||
|
HANDLE hList[eNumHandles]= { INVALID_HANDLE_VALUE };
|
||
|
|
||
|
if (m_strDirPath.GetLength() == 0)
|
||
|
{
|
||
|
SetThreadStatus(eWaitDirPath);
|
||
|
|
||
|
// Block this thread until notification that the directory path has been set.
|
||
|
::WaitForSingleObject( m_hevMonitorRequested, INFINITE);
|
||
|
}
|
||
|
|
||
|
SetThreadStatus(eRun);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if (RUNNING_ONLINE_TS())
|
||
|
{
|
||
|
// The DirPathChanged flag should be set here, enforce it if not.
|
||
|
ASSERT( m_bDirPathChanged );
|
||
|
if (!m_bDirPathChanged)
|
||
|
m_bDirPathChanged= true;
|
||
|
}
|
||
|
|
||
|
// Wait for an explicit wakeup.
|
||
|
hList[eHev] = m_hevMonitorRequested;
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
if (m_bShuttingDown)
|
||
|
{
|
||
|
UNLOCKOBJECT();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (m_bDirPathChanged)
|
||
|
{
|
||
|
|
||
|
#ifndef LOCAL_TROUBLESHOOTER
|
||
|
// Set the directory to be monitored.
|
||
|
if (hList[eDirChange] != INVALID_HANDLE_VALUE)
|
||
|
::FindCloseChangeNotification( hList[eDirChange] );
|
||
|
while (true)
|
||
|
{
|
||
|
// handle to monitor for change in the resource directory
|
||
|
hList[eDirChange] = ::FindFirstChangeNotification(m_strDirPath,
|
||
|
TRUE, // monitor subdirectories (for multilingual)
|
||
|
FILE_NOTIFY_CHANGE_LAST_WRITE
|
||
|
);
|
||
|
|
||
|
if (hList[eDirChange] == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
// resource directoty does not exist.
|
||
|
// Track creation of directories in upper directory
|
||
|
// - it might be resource directory
|
||
|
|
||
|
bool bFail = false;
|
||
|
CString strUpperDir = m_strDirPath; // directory above resource directory (m_strDirPath)
|
||
|
|
||
|
if ( strUpperDir[strUpperDir.GetLength()-1] == _T('\\')
|
||
|
|| strUpperDir[strUpperDir.GetLength()-1] == _T('/'))
|
||
|
{
|
||
|
strUpperDir = strUpperDir.Left(strUpperDir.GetLength() ? strUpperDir.GetLength()-1 : 0);
|
||
|
}
|
||
|
|
||
|
int slash_last = max(strUpperDir.ReverseFind(_T('\\')),
|
||
|
strUpperDir.ReverseFind(_T('/')));
|
||
|
|
||
|
if (-1 != slash_last)
|
||
|
{
|
||
|
strUpperDir = strUpperDir.Left(slash_last);
|
||
|
|
||
|
hList[eDirChange] = ::FindFirstChangeNotification(strUpperDir,
|
||
|
TRUE, // monitor subdirectories (for multilingual)
|
||
|
FILE_NOTIFY_CHANGE_DIR_NAME
|
||
|
);
|
||
|
if (hList[eDirChange] == INVALID_HANDLE_VALUE)
|
||
|
bFail = true;
|
||
|
}
|
||
|
else
|
||
|
bFail = true;
|
||
|
|
||
|
if (!bFail)
|
||
|
{
|
||
|
// We have a valid handle, exit this loop.
|
||
|
SetThreadStatus(eRun);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// typically would mean none of resource directory or its upper
|
||
|
// directory is valid, log this.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
m_strDirPath, _T(""),
|
||
|
EV_GTS_ERROR_CANT_FILE_NOTIFY );
|
||
|
|
||
|
SetThreadStatus(eWaitDirPath);
|
||
|
|
||
|
// Block this thread until notification that the directory path
|
||
|
// has been correctly set. Unlock the object so that the event
|
||
|
// can be set.
|
||
|
UNLOCKOBJECT();
|
||
|
::WaitForSingleObject( m_hevMonitorRequested, INFINITE);
|
||
|
LOCKOBJECT();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We have a valid handle, exit this loop.
|
||
|
SetThreadStatus(eRun);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
m_bDirPathChanged = false;
|
||
|
if (m_pTrackLst)
|
||
|
delete m_pTrackLst;
|
||
|
m_pTrackLst = new CFileTracker;
|
||
|
|
||
|
if (RUNNING_ONLINE_TS())
|
||
|
m_pTrackLst->AddFile(m_strLstPath);
|
||
|
|
||
|
if (m_pTrackErrorTemplate)
|
||
|
delete m_pTrackErrorTemplate;
|
||
|
m_pTrackErrorTemplate = new CFileTracker;
|
||
|
|
||
|
if (RUNNING_ONLINE_TS())
|
||
|
m_pTrackErrorTemplate->AddFile(m_strErrorTemplatePath);
|
||
|
|
||
|
UNLOCKOBJECT();
|
||
|
ReadErrorTemplate();
|
||
|
LstFileDrivesTopics();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UNLOCKOBJECT();
|
||
|
|
||
|
if (m_pTrackLst && m_pTrackLst->Changed())
|
||
|
LstFileDrivesTopics();
|
||
|
|
||
|
if (m_pTrackErrorTemplate && m_pTrackErrorTemplate->Changed( false ))
|
||
|
ReadErrorTemplate();
|
||
|
}
|
||
|
|
||
|
LOCKOBJECT();
|
||
|
for (vector<CTopicFileTracker>::iterator itTopicFiles = m_arrTrackTopic.begin();
|
||
|
itTopicFiles != m_arrTrackTopic.end();
|
||
|
itTopicFiles ++
|
||
|
)
|
||
|
{
|
||
|
#ifdef LOCAL_TROUBLESHOOTER
|
||
|
if (m_bDirPathChanged)
|
||
|
#else
|
||
|
if (itTopicFiles->Changed())
|
||
|
#endif
|
||
|
m_TopicShop.BuildTopic(itTopicFiles->GetTopicInfo().GetNetworkName());
|
||
|
if (m_bShuttingDown)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (RUNNING_ONLINE_TS())
|
||
|
{
|
||
|
// Check if any of the alternate template files need to be reloaded.
|
||
|
for (vector<CTemplateFileTracker>::iterator itTemplateFiles = m_arrTrackTemplate.begin();
|
||
|
itTemplateFiles != m_arrTrackTemplate.end();
|
||
|
itTemplateFiles ++
|
||
|
)
|
||
|
{
|
||
|
if (itTemplateFiles->Changed())
|
||
|
m_TopicShop.BuildTemplate( itTemplateFiles->GetTemplateName() );
|
||
|
if (m_bShuttingDown)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
::ResetEvent(m_hevMonitorRequested);
|
||
|
|
||
|
SetThreadStatus(eWaitChange);
|
||
|
UNLOCKOBJECT();
|
||
|
|
||
|
DWORD dwNotifyObj = WaitForMultipleObjects (
|
||
|
eNumHandles,
|
||
|
hList,
|
||
|
FALSE, // only need one object, not all
|
||
|
INFINITE);
|
||
|
|
||
|
SetThreadStatus(eBeforeWaitChange);
|
||
|
|
||
|
// Ideally we would update files here.
|
||
|
// Unfortunately, we get a notification that someone has _started_
|
||
|
// writing to a file, not that they've finished, so we have to put in
|
||
|
// an artificial delay.
|
||
|
// We must let the system "settle down".
|
||
|
while (
|
||
|
#ifndef LOCAL_TROUBLESHOOTER
|
||
|
dwNotifyObj == WAIT_OBJECT_0+eDirChange &&
|
||
|
#endif
|
||
|
!m_bShuttingDown)
|
||
|
{
|
||
|
#ifndef LOCAL_TROUBLESHOOTER
|
||
|
// wait for the next change
|
||
|
if (FindNextChangeNotification( hList[eDirChange] ) == FALSE)
|
||
|
{
|
||
|
// 1) we don't believe this will ever occur
|
||
|
// 2) After a moderate amount of research, we have no idea how
|
||
|
// to recover from it if it does occur.
|
||
|
// SO: unless we ever actually see this, we're not going to waste
|
||
|
// more time researching a recovery strategy. Just throw an exception,
|
||
|
// effectively terminating this thread.
|
||
|
throw CGenSysException( __FILE__, __LINE__, m_strDirPath,
|
||
|
EV_GTS_ERROR_WAIT_NEXT_NFT );
|
||
|
}
|
||
|
#endif
|
||
|
SetThreadStatus(eWaitSettle);
|
||
|
|
||
|
dwNotifyObj = WaitForMultipleObjects (
|
||
|
eNumHandles,
|
||
|
hList,
|
||
|
FALSE, // only need one object, not all
|
||
|
m_secsReloadDelay * 1000); // convert to milliseconds
|
||
|
}
|
||
|
if (dwNotifyObj == WAIT_FAILED)
|
||
|
{
|
||
|
// 1) we don't believe this will ever occur
|
||
|
// 2) After a moderate amount of research, we have no idea how
|
||
|
// to recover from it if it does occur.
|
||
|
// SO: unless we ever actually see this, we're not going to waste
|
||
|
// more time researching a recovery strategy. Just throw an exception,
|
||
|
// effectively terminating this thread.
|
||
|
throw CGenSysException( __FILE__, __LINE__, _T("Unexpected Return State"),
|
||
|
EV_GTS_DEBUG );
|
||
|
}
|
||
|
SetThreadStatus(eRun);
|
||
|
}
|
||
|
}
|
||
|
catch (CGenSysException& x)
|
||
|
{
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
x.GetErrorMsg(), x.GetSystemErrStr(),
|
||
|
x.GetErrorCode() );
|
||
|
}
|
||
|
catch (CGeneralException& x)
|
||
|
{
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
x.GetErrorMsg(), _T("General exception"),
|
||
|
x.GetErrorCode() );
|
||
|
}
|
||
|
catch (bad_alloc&)
|
||
|
{
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
_T(""), _T(""), EV_GTS_CANT_ALLOC );
|
||
|
}
|
||
|
catch (exception& x)
|
||
|
{
|
||
|
// Catch any STL exceptions thrown so that Terminate() is not called.
|
||
|
CString str;
|
||
|
CString ErrStr;
|
||
|
|
||
|
// Attempt to pull any system error code.
|
||
|
ErrStr.Format( _T("%ld"), ::GetLastError() );
|
||
|
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
CCharConversion::ConvertACharToString(x.what(), str),
|
||
|
ErrStr,
|
||
|
EV_GTS_GENERIC_PROBLEM );
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
// Catch any other exception thrown.
|
||
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
||
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
||
|
SrcLoc.GetSrcFileLineStr(),
|
||
|
_T(""), _T(""),
|
||
|
EV_GTS_GEN_EXCEPTION );
|
||
|
}
|
||
|
#ifndef LOCAL_TROUBLESHOOTER
|
||
|
if (hList[eDirChange] != INVALID_HANDLE_VALUE)
|
||
|
::FindCloseChangeNotification( hList[eDirChange] );
|
||
|
#endif
|
||
|
SetThreadStatus(eExiting);
|
||
|
}
|
||
|
|
||
|
// For general use (not part of DirectoryMonitorTask thread)
|
||
|
// Typically, first call to this comes _before_ first call to SetResourceDirectory;
|
||
|
// This allows caller to set reload delay before triggering any action on
|
||
|
// DirectoryMonitorTask thread.
|
||
|
void CDirectoryMonitor::SetReloadDelay(DWORD secsReloadDelay)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
m_secsReloadDelay = secsReloadDelay;
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// For general use (not part of DirectoryMonitorTask thread)
|
||
|
// Allows indicating that the resource directory has changed
|
||
|
// Until this is called, the DirectoryMonitorTask thread really won't do anything
|
||
|
void CDirectoryMonitor::SetResourceDirectory(const CString & strDirPath)
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
if (strDirPath != m_strDirPath)
|
||
|
{
|
||
|
m_strDirPath = strDirPath;
|
||
|
m_strLstPath = strDirPath + LSTFILENAME;
|
||
|
m_strErrorTemplatePath = strDirPath + k_strErrorTemplateFileName;
|
||
|
m_bDirPathChanged = true;
|
||
|
::SetEvent(m_hevMonitorRequested);
|
||
|
}
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// Must be called on DirectoryMonitorTask thread.
|
||
|
void CDirectoryMonitor::AckShutDown()
|
||
|
{
|
||
|
LOCKOBJECT();
|
||
|
::SetEvent(m_hevThreadIsShut);
|
||
|
UNLOCKOBJECT();
|
||
|
}
|
||
|
|
||
|
// Main routine of a thread responsible for monitoring the directory.
|
||
|
// INPUT lpParams
|
||
|
// Always returns 0.
|
||
|
/* static */ UINT WINAPI CDirectoryMonitor::DirectoryMonitorTask(LPVOID lpParams)
|
||
|
{
|
||
|
reinterpret_cast<CDirectoryMonitor*>(lpParams)->Monitor();
|
||
|
reinterpret_cast<CDirectoryMonitor*>(lpParams)->AckShutDown();
|
||
|
return 0;
|
||
|
}
|
||
|
|