Reanimator 进程恢复实用程序






4.41/5 (4投票s)
描述了一个进程恢复实用程序,该程序可以看作是使用多线程、线程间通信和同步技术的一个简单示例。
概述
本文介绍了一个使用 Visual C++ 和 MFC 实现的进程恢复实用程序。该实用程序的源代码可以被视为使用多线程、线程间通信和同步技术的一个非常简单的例子。
引言
最近,我有一个任务,需要实现一个实用程序,它可以监控应用程序的可用性,并在应用程序崩溃或被用户意外关闭时自动重启该应用程序。该实用程序必须确保应用程序始终在运行。我决定将其创建一个相对通用的工具,可以用于任何类型的应用程序,并且其代码库可以用于其他类似的解决方案。我将这个实用程序命名为 Reanimator。
起初,我打算使用 C# 和 .NET Framework 来实现 Reanimator,因为这是最简单快捷的方法(至少对我来说),但我希望避免强制用户在他们的 PC 上安装 .NET Framework 仅仅是为了使用这个小小的辅助实用程序。因此,我决定使用 C++/MFC 来创建它。
一般描述
这个解决方案的核心思想如下。用户不是直接启动目标应用程序,而是通过 Reanimator 实用程序启动。Reanimator 实用程序会保存启动进程的句柄,然后定期使用 WaitForSingleObject
Win32 API 函数检查进程的可用性。检查循环在一个单独的线程中执行,我将该线程称为“监控线程”。一旦监控线程检测到进程不再可用,它会使用启动应用程序时相同的命令行参数再次启动应用程序,并继续监控已启动应用程序的进程。所有此类自动进程恢复的事件都会记录到文本日志文件(Reanimator.log,位于实用程序可执行文件的目录中),并在实用程序的窗口中显示,以便用户可以跟踪其“复活”活动。
Reanimator 提供了一个选项,允许在 Windows 启动时自动启动该实用程序(更确切地说,是在用户登录后启动 Windows 用户会话时;使用 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run 键来自动启动该实用程序)。当 Reanimator 以这种方式自动启动时,它还会自动启动之前通过该实用程序运行的应用程序,并开始监控该应用程序的进程。
命令行和自动启动选项的状态会在实用程序关闭时自动保存(作为应用程序设置,存储在当前用户的配置文件中)。关闭实用程序不会导致正在运行的应用程序停止,但应用程序会继续运行,当然也不会被监控。
内部实现(主类)
所有进程恢复功能都实现在 CReanimatorWorker
类中。该类仅公开两个公共方法,如下所示:
// 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);
为了使用此类,只需创建一个类实例(使用其默认构造函数,这是唯一的构造函数),然后调用 Run
方法,将用于启动(和重新启动)应用程序的命令行字符串作为该方法的输入参数。
使用 Stop
方法,可以停止监控线程,如果它不再需要(例如,当用户希望关闭应用程序而不希望该应用程序被监控线程自动重启时)。不过,当 CReanimatorWorker
对象不再需要时,不一定需要调用 Stop
方法,因为该类的析构函数总会在监控线程运行时停止它。
在监控线程检测到进程不存在并尝试重启进程后,它会立即通知主线程(调用 CReanimatorWorker::Run
方法的线程)有关此事件,并指示进程恢复的结果。通知是通过向主线程发送消息(使用 CWinThread::PostThreadMessage
方法)来完成的。消息的 ID 由 CReanimatorWorker
类作为名为 REANIMATED_MSG_ID
的常量公开。
static const UINT REANIMATED_MSG_ID = WM_USER + 1;
消息的 wParam
参数包含一个 BOOL
值,指示进程恢复是否成功(TRUE
)或失败(FALSE
)。如果恢复失败(如果进程未能成功重启),lParam
参数指向一个 CString
类型的对象指针,其中包含导致失败的错误的描述。在这种情况下,监控线程会自动停止,因为通常情况下,消除问题的原因需要用户干预,因此继续尝试启动进程是没有意义的。通过这种通知机制,主线程可以跟踪所有自动进程恢复的事件。
实现说明
- 在启动(或重新启动)进程后,启动过程会等待几秒钟,然后检查进程是否仍然可用,以确保进程在启动后不会立即终止(例如,原因可能是应用程序初始化失败或其他类似情况)。只有当进程在一段时间后仍然可用时,才认为应用程序已成功启动。
CReanimatorWorker
类有一个私有成员变量,名为m_bStopRequested
,主线程和监控线程都可以访问它。为了使对该变量的访问是线程安全的,使用了CCriticalSection
类。// 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(); }
- 实用程序的 Release 生成配置会生成一个与 MFC 库静态链接的可执行文件。这使得可以将实用程序作为单个可执行文件分发,而无需担心任何 MFC 运行时 DLL。
后记
很久以前,我几乎开始为 Windows 服务应用程序的恢复实现类似的解决方案。幸运的是,我及时发现,从 Windows 2000 开始,Windows 操作系统就已经提供了此功能。因此,我没有“重复造轮子”。通常,需要持续在后台运行的应用程序被实现为服务,并且这些应用程序的恢复没有问题。在我的情况下,我需要解决普通(非服务)应用程序的恢复问题,这就是实现这个小程序的原因。谁知道呢,也许有人会遇到同样的问题,并觉得我的实用程序(或其源代码)有用。
欢迎对文章和源代码提出任何意见和建议。