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:
void Run(CString &csCmdLine) throw (...);
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
:
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
- 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.
- 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.
CCriticalSection m_critSect;
bool m_bStopRequested;
bool GetStopRequested(void)
{
m_critSect.Lock();
bool bStopRequested = m_bStopRequested;
m_critSect.Unlock();
return bStopRequested;
}
void SetStopRequested(bool value)
{
m_critSect.Lock();
m_bStopRequested = value;
m_critSect.Unlock();
}
- 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.