Click here to Skip to main content
15,867,704 members
Articles / Programming Languages / C++
Article

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 47.9K   941   20   10
Description of a process recovery utility, which can be considered as a simple example of using multithreading, inter-thread communication, and synchronization techniques.

Sample Image - Reanimator_screenshot.jpg

Overview

This article provides the description of a process recovery utility, which was implemented using Visual C++ and MFC. The source code of the utility can be considered as a very simple example of using multithreading, inter-thread communication, and synchronization techniques.

Introduction

Recently, I had a task of realization of a utility, which would monitor the availability of an application and automatically restart this application in case it was crashed or accidentally closed by the user. The utility had to assure that the application was always running. I decided to create it as a more or less universal tool, which could be used with any kind of application and whose code base could be used in other similar solutions. I called this utility Reanimator.

First, I was going to realize Reanimator using C# and the .NET Framework, as the easiest and the quickest way of doing it (at least for me), but I preferred to avoid forcing the users to install the .NET Framework on their PCs only in order to use this little auxiliary utility. So, it was decided to create it using C++/MFC.

General Description

The main idea of this solution is the following. The user starts the target application, not directly, but through the Reanimator utility, which stores the handle of the started process and then, periodically, checks the availability of the process using the WaitForSingleObject Win32 API function. The checking loop is performed in a separate thread, which I’ll further refer to as the “monitoring thread”. As soon as the monitoring thread detects that the process is not available anymore, it starts the application again using the same command line which was used when the application was started initially, and continues monitoring the process of the started application. All the events of such automatic process recovery are logged to a text log file (Reanimator.log, in the directory of the utility’s executable file) and displayed in the utility’s window, so that the user could track its “reanimation” activity.

Reanimator provides an option allowing automatic startup of the utility when Windows starts (when the Windows user session is started after the user’s logon, to be more precise; HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run key is used for automatic startup of the utility). When Reanimator is automatically started in such a way, it also automatically starts the application, which was previously run using this utility, and begins monitoring the process of this application.

The command line and the status of the automatic startup option are automatically saved (as application settings, which are stored in the current user’s profile) when the utility is closed. Closing the utility doesn’t cause the running application to be stopped, but the application remains running without being monitored, of course.

Internal Implementation (Main Class)

All the process recovery functionality is implemented in the class CReanimatorWorker. This class exposes only two public methods, which are presented below:

C++
// 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 Run(CString &csCmdLine) throw (...);

// Stops monitoring the application (but doesn't terminate the application)

void Stop(void);

In order to use this class, it is enough to create an instance of the class (using its default constructor, which is the only constructor) and call the Run method, passing the command line string for starting (and restarting) the application as an input parameter for this method.

Using the Stop method, it is possible to stop the monitoring thread if it is not needed anymore (for example, when the user wants to close the application without having this application to be automatically restarted by the monitoring thread). Though, it is not necessary to call the Stop method when the CReanimatorWorker object is not needed anymore, because the destructor of this class always stops the monitoring thread if it is running.

Immediately after the monitoring thread detects that the process is absent and tries to restart it, it notifies the main thread (the thread which called the CReanimatorWorker::Run method) about this event, indicating the results of process recovery. The notification is performed by sending a message to the main thread (using the CWinThread::PostThreadMessage method). The ID of the message is exposed by the class CReanimatorWorker as a constant named REANIMATED_MSG_ID:

C++
static const UINT REANIMATED_MSG_ID = WM_USER + 1;

The wParam parameter of the message contains a BOOL value indicating if the process recovery was successful (TRUE) or not (FALSE). In the case of a recovery failure (if the process was not restarted successfully), the lParam parameter represents a pointer to an object of type CString, containing the description of the error which caused the failure. In this case, the monitoring thread is automatically stopped because, usually, the elimination of the cause of the problem would require the intervention of the user anyway, and therefore it's useless to continue subsequent attempts of starting the process. Using this notification mechanism, the main thread can track all the events of the automatic process recovery.

Implementation Notes

  1. After starting (or restarting) the process, the starting procedure waits for a few seconds, and then checks if the process is still available, in order to assure that the process was not terminated immediately after it was started (for example, the cause could be an application initialization failure or something like that). Only if the process remains available after a while, it is considered that the application was started successfully.
  2. The CReanimatorWorker class has a private member variable called m_bStopRequested, which can be accessed by both the main and the monitoring threads. To make the access to this variable thread-safe, the CCriticalSection class is used.
    C++
    // critical section for accessing m_bStopRequested,
    // which can be accessed by both
    // the main thread and the worker (monitoring) thread
    
    CCriticalSection m_critSect;
    
    
    // Flag indicating that the worker (monitoring) thread needs to be stopped.
    
    // IMPORTANT: Must be accessed only using thread-safe helper methods
    // GetStopRequested and SetStopRequested
    
    bool m_bStopRequested;
    
    
    // Thread-safe access to m_bStopRequested
    
    bool GetStopRequested(void)
    {
        m_critSect.Lock();
        bool bStopRequested = m_bStopRequested;
        m_critSect.Unlock();
    
        return bStopRequested;
    }
    
    // Thread-safe access to m_bStopRequested
    
    void SetStopRequested(bool value)
    {
        m_critSect.Lock();
        m_bStopRequested = value;
        m_critSect.Unlock();
    }
    
  3. The Release build configuration of the utility produces an executable which is statically linked with the MFC libraries. This makes it possible to distribute the utility as a single executable file, without the need to bother about any MFC runtime DLLs.

Afterword

Once, some time ago, I almost began the realization of a similar solution for the recovery of a Windows service application. Fortunately, I timely discovered that such a feature is available as part of the Windows operating system, starting from Windows 2000. So, I didn’t have to “reinvent the wheel”. Normally, applications which are expected to be running constantly in the background are realized as services, and there are no problems with the recovery of such applications. In my case, I had to solve the recovery problem for a common (not service) application, and that was the reason for the realization of this little utility. Who knows, may be someone will face the same problem and will find my utility (or its source code) useful.

Any remarks and comments on the article and the source code are welcome.

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

 
GeneralJust a note Pin
peterchen21-Jan-06 21:09
peterchen21-Jan-06 21:09 
QuestionRe: Just a note Pin
Maxim Izbrodin22-Jan-06 8:47
Maxim Izbrodin22-Jan-06 8:47 
AnswerRe: Just a note Pin
peterchen22-Jan-06 9:08
peterchen22-Jan-06 9:08 
QuestionRe: Just a note Pin
Maxim Izbrodin22-Jan-06 9:21
Maxim Izbrodin22-Jan-06 9:21 
AnswerRe: Just a note Pin
peterchen22-Jan-06 9:31
peterchen22-Jan-06 9:31 
Yes - which compiler / CString class are you using? Are you by change compiling with _UNICODE ?



Some of us walk the memory lane, others plummet into a rabbit hole
boost your code || Fold With Us! || sighist

GeneralRe: Just a note Pin
Maxim Izbrodin22-Jan-06 9:37
Maxim Izbrodin22-Jan-06 9:37 
GeneralRe: Just a note Pin
Blake Miller23-Jan-06 9:58
Blake Miller23-Jan-06 9:58 
QuestionRe: Just a note Pin
Maxim Izbrodin22-Jan-06 9:12
Maxim Izbrodin22-Jan-06 9:12 
GeneralDoesn't matter how you pass CString to a function Pin
Damir Valiulin24-Jan-06 6:16
Damir Valiulin24-Jan-06 6:16 
GeneralRe: Doesn't matter how you pass CString to a function Pin
peterchen24-Jan-06 8:20
peterchen24-Jan-06 8:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.