Click here to Skip to main content
15,886,069 members
Articles / Programming Languages / C++

Reanimator utility for process recovery

Rate me:
Please Sign up or sign in to vote.
4.41/5 (4 votes)
20 Jan 2006CPOL5 min read 48K   941   20  
Description of a process recovery utility, which can be considered as a simple example of using multithreading, inter-thread communication, and synchronization techniques.
// 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions