Часть 5. Обеспечение стабильности приложений: механизм Application Restart and Recovery
В предыдущей части данной статьи мы рассказывали об обеспечении стабильной работы приложений, привели ряд рекомендаций и обсудили различные механизмы, предоставляемые для этой цели операционной системой. В настоящей статье мы поговорим о механизме Application Restart and Recovery.
Механизм Application Restart and Recovery
Все современные приложения, написанные для работы под управлением операционных систем Microsoft Windows Vista, Windows 7, а также претендующие на совместимость с будущими версиями клиентских операционных систем, должны включать базовую поддержку механизма Application Restart and Recovery. Для этого обработчик событий должен уметь обрабатывать следующие события, связанные с завершением работы приложения, и выполнять описанные ниже действия:
- сообщение WM_QUERYENDSESSION с параметром LPARAM = ENDSESSION_CLOSEAPP(0x1) — приложение с пользовательским интерфейсом должно немедленно ответить на это сообщение отсылкой TRUE и начать подготовку к перезапуску: конкретные действия зависят от приложения, чаще всего это сохранение состояния, содержимого документов и т.п.;
- сообщение WM_ENDSESSION с параметром LPARAM = ENDSESSION_CLOSEAPP(0x1) — приложение должно ответить на это сообщение отсылкой 0 в течение 30 с после получения сообщения и завершить работу;
- нажатие комбинации клавиш CTRL+C — консольные приложения, получающие сообщение о нажатии данной комбинации клавиш, должны немедленно завершить работу.
Полноценная поддержка механизма Application Restart and Recovery включает регистрацию приложения в специальной структуре, поддерживаемой на уровне ядра операционной системы. Для этого следует использовать функцию RegisterApplicationRestart(), которой в качестве параметров передаются строка и набор флагов. Строка может содержать опции, которые укажут приложению на то, что оно перезапущено и, например, требуется загрузить сохраненные данные. Флаг может иметь либо нулевое значение, либо одно из значений, приведенных в табл. 1.
Таблица 1. Флаги функции RegisterApplicationRestart()
Флаг
Значение
Описание
Не перезапускать процесс, если он был завершен в результате возникновения необработанного исключения
Не перезапускать процесс, если он был завершен в результате зависания приложения
Не перезапускать процесс, если он был завершен в результате установки обновления ОС
Не перезапускать процесс, если он был завершен в результате перезапуска компьютера после установки обновления ОС
Таблица 2. Функции для расширенной поддержки механизма Application Restart and Recovery
Функция
Описание
Указывает на то, что вызывающее приложение завершило восстановление данных
Указывает на то, что вызывающее приложение продолжает восстанавливать данные
Возвращает указатель на косвенновызываемую функцию, зарегистрированную для указанного процесса
Возвращает информацию о перезапуске приложения для указанного процесса
Регистрирует косвенновызываемую функцию, используемую для восстановления приложения
Удаляет информацию о приложении из списка восстанавливаемых приложений
Удаляет информацию о приложении из списка перезагружаемых приложений
На уровне ядра операционной системы также существует ряд дополнительных функций, которые можно использовать для расширенной поддержки механизма Application Restart and Recovery (табл. 2).
Ключевой здесь является возможность регистрации косвенновызываемой функции — ApplicationRecoveryCallback, которая может использоваться для сохранения данных и состояния приложения в тех случаях, когда приложение зависает или возникает необрабатываемая критическая ошибка. После вызова зарегистрированной косвенновызываемой функции приложение должно периодически (интервал указывается при регистрации функции, по умолчанию — каждые 5 с) вызывать функцию ApplicationRecoveryInProgress(), указывая на то, что идет процесс сохранения данных, а после завершения сохранения — функцию ApplicationRecoveryFinished().
Помимо этого существует ряд функций, с помощью которых приложение может при необходимости принудительно завершать и перезапускать другие приложения (при наличии соответствующих прав).
К таким функциям относятся функции, перечисленные в табл. 3, — все они реализованы в библиотеке Rstrtmgr.dll, а их прототипы описаны в файле RestartManager.h.
Таблица 3. Функции для принудительного завершения и перезапуска приложений
Функция
Описание
Изменяет действия, связанные с принудительным завершением работы приложений и их перезапуском
Стартует новую сессию Restart Manager. Поддерживается до 64 одновременных сессий
Включает процесс, связанный с приложением, в уже существующую сессию Restart Manager
Завершает сессию Restart Manager
Регистрирует ресурсы — имена файлов, имена сервисов или структуры RM_UNIQUE_PROCESS в сессии Restart Manager
Используется программами установки приложений для получения списка приложений, связанных с зарегистрированными ресурсами, и их текущего статуса
Запрашивает статус изменений состояния принудительного завершения и перезапуска
Инициирует принудительное завершение приложения или процесса
Удаляет предварительно внесенные изменения в действия, связанные с принудительным завершением работы приложений и их перезапуском
Перезапускает приложения и сервисы, которые были принудительно завершены функцией RmShutdown() и зарегистрированы с помощью функции RegisterApplicationRestart()
Отменяет действия функций RmGetList(), RmShutdown() и RmRestart()
Ниже показан пример консольного приложения, использующего механизм Restart Manager для принудительного завершения и перезапуска процесса — в данном примере применяется утилита Calculator.
Базовый пример использования механизма Restart Manager
int _cdecl wmain()
DWORD dwErrCode = ERROR_SUCCESS;
DWORD dwSessionHandle = 0xFFFFFFFF;
// CCH_RM_SESSION_KEY: Число символов в названии сессии
// Число регистрируемых файлов
DWORD dwFiles = 2;
// Регистрация двух файлов — для 32разрядной и 64разрядной версии
UINT nAffectedApps = 0;
UINT nProcInfoNeeded = 0;
RM_REBOOT_REASON dwRebootReasons = RmRebootReasonNone;
RM_PROCESS_INFO *rgAffectedApps = NULL;
// Начало сессии Restart Manager
dwErrCode = RmStartSession(&dwSessionHandle, 0, sessKey);
if (ERROR_SUCCESS != dwErrCode)
// Регистрация файлов в Restart Manager
// Ниже регистрируются два исполняемых файла.
// Также возможна регистрация файлов, процессов и сервисов
if (ERROR_SUCCESS != dwErrCode)
// Получение списка приложений и сервисов
if (ERROR_SUCCESS == dwErrCode)
// Успешное выполнение RmGetList()
if (ERROR_MORE_DATA != dwErrCode)
// Неудачное выполнение RmGetList()
// с ошибкой, отличной от ERROR_MORE_DATA
// Запрос дополнительных данных
if (NULL != rgAffectedApps)
rgAffectedApps = new RM_PROCESS_INFO[nAffectedApps];
> while ((ERROR_MORE_DATA == dwErrCode) && (nRetry ++ < 3));
if (ERROR_SUCCESS != dwErrCode)
if (RmRebootReasonNone != dwRebootReasons)
// Невозможно выполнить перезагрузку
// Список rgAffectedApps содержит приложения и сервисы
// Число приложений и сервисов — в переменной nAffectedApps
// Основной код помещается здесь
// Завершение работы всех приложений и сервисов из списка
dwErrCode = RmShutdown(dwSessionHandle, 0, NULL);
if (ERROR_SUCCESS != dwErrCode)
// Ресурсы освобождены — возможность обновления/замены
// Основной код помещается здесь
// Перезапуск приложений и сервисов после обновления файлов
dwErrCode = RmRestart(dwSessionHandle, 0, NULL);
if (ERROR_SUCCESS != dwErrCode)
if (NULL != rgAffectedApps)
if (0xFFFFFFFF != dwSessionHandle)
// Очистка сессии Restart Manager
В следующем примере показано, как реализовать автоматический перезапуск приложения за счет регистрации в соответствующем сервисе Application Restart and Recovery:
const wchar_t* argv[],
const wchar_t* envp[]
HRESULT hr = E_FAIL;
// Обычный запуск или перезапуск?
0 == _wcsicmp (argv[1], L"/restarted"))