|
// Copyright � 2005 Maxim Izbrodin
#include "StdAfx.h"
#include "resource.h"
#include "ReanimatorWorker.h"
// Destructor. Stops the working (monitoring) thread if it's running
CReanimatorWorker::~CReanimatorWorker(void)
{
if (m_pWorkerThread != NULL)
{
StopThread(threadTerminationTimeout);
}
}
// Thread procedure for the worker thread.
// Monitores the application by calling periodically CheckProcess.
// The parameter is the object of CReaminatorWorker class, which is the owner of the thread.
// (Otherwise we cannot access the non-static class members, since this method is static)
UINT CReanimatorWorker::WorkerThreadProc(LPVOID pParam)
{
// Get the pointer to the CReanimatorWorker object, which is the owner of this thread
CReanimatorWorker* worker = (CReanimatorWorker*) pParam;
const int nProcessCheckPeriod = 10; // process availability check period (seconds)
const int nStopRequestedCheckPeriod = 500; // period for checking request for stopping the thread (milliseconds)
CTime lastProcessCheckTime = CTime::GetCurrentTime();
CTimeSpan elapsedAfterProcessCheck;
while (!worker->GetStopRequested())
{
elapsedAfterProcessCheck = CTime::GetCurrentTime() - lastProcessCheckTime;
// if its time to check the process' availability
if (elapsedAfterProcessCheck.GetTotalSeconds() > nProcessCheckPeriod)
{
worker->CheckProcess();
lastProcessCheckTime = CTime::GetCurrentTime();
}
::Sleep(nStopRequestedCheckPeriod);
}
// exit thread
worker->m_pWorkerThread = NULL;
return 0;
}
// Starts the application, using the command line passed as the parameter,
// and starts the monitoring thread.
// In case of failure throws an exception of CString type with the object containing
// the description of the problem.
void CReanimatorWorker::Run(CString &csCmdLine)
{
// Check if the monitoring thread is already running
if (m_pWorkerThread != NULL)
{
CString csErrorString;
csErrorString.LoadString(IDS_ALREADY_RUNNING);
throw csErrorString;
}
// Set new command line
m_csCmdLine = csCmdLine;
// Check if the command line is empty
if (m_csCmdLine.IsEmpty())
{
CString csErrorString;
csErrorString.LoadString(IDS_EXE_PATH_EMPTY);
throw csErrorString;
}
// Save the pointer to the main thread.
// It will be used for sending notification messages to this thread from the worker (monitoring) thread
m_pMainThread = AfxGetThread();
// Assure that the m_bStopRequested is not set
SetStopRequested(false);
// Start the process
StartProcess(); // throws CString in case of error
// Create the worker (monitoring) thread
m_pWorkerThread = AfxBeginThread(CReanimatorWorker::WorkerThreadProc, this, THREAD_PRIORITY_LOWEST);
}
// Stops the worker thread
void CReanimatorWorker::Stop()
{
StopThread(INFINITE);
}
// Checks the monitored application status and starts it if it's not running anymore
void CReanimatorWorker::CheckProcess()
{
// Check if the application is still running
DWORD res = ::WaitForSingleObject(m_hProcess, 0);
if (res == WAIT_TIMEOUT)
{
// The process is still running. Do nothing
return;
}
// The process is not running anymore
// Try to start it again
try
{
StartProcess();
}
catch (CString csErrorDetails)
{
// starting process failed
// we need to stop the thread execution
SetStopRequested(true);
// communicate about the problem to the main thread
m_pMainThread->PostThreadMessage(REANIMATED_MSG_ID, FALSE, (LPARAM) new CString(csErrorDetails));
}
// communicate about the successful reanimation results
m_pMainThread->PostThreadMessage(REANIMATED_MSG_ID, TRUE, 0);
}
// Starts the application using the command line stored in m_csCmdLine, checks if the process
// was started successfully and saves the handle of the process to m_hProcess.
// In case of failure throws an exception of CString type with the object containing
// the description of the problem.
void CReanimatorWorker::StartProcess(void)
{
if (m_hProcess != NULL)
{
::CloseHandle(m_hProcess);
m_hProcess = NULL;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (!::CreateProcess(NULL, m_csCmdLine.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
// an error occured when trying to start the application
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
throw CString((LPTSTR) lpMsgBuf);
}
// we don't need the thread handle
::CloseHandle(pi.hThread);
// wait some a few seconds and check that the started process still rimains
// (to be sure that the application was started correctly and was not closed immediately)
const DWORD runningOkTimeout = 5*1000; // msecs
DWORD res = ::WaitForSingleObject(pi.hProcess, runningOkTimeout);
if (res != WAIT_TIMEOUT)
{
// the process was terminated
::CloseHandle(pi.hProcess);
CString csDetails;
csDetails.LoadString(IDS_IMMEDIATELY_CLOSED);
throw csDetails;
}
// the application was successfully started
// save the handle to process handle
m_hProcess = pi.hProcess;
}
// Stops the worker (monitoring) thread. If the thread is not stopped after the timout, which is
// indicated in milliseconds as a method parameter, then the thread is killed.
void CReanimatorWorker::StopThread(DWORD timeoutMs)
{
SetStopRequested(true);
if (m_pWorkerThread != NULL)
{
HANDLE hThread = m_pWorkerThread->m_hThread;
DWORD res = ::WaitForSingleObject(hThread, timeoutMs);
if (res == WAIT_TIMEOUT)
{
::TerminateThread(hThread, 1);
}
m_pWorkerThread = NULL;
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.