Модернизация приложений

Часть 5. Обеспечение стабильности приложений: механизм Application Restart and Recovery

Алексей Федоров (сотрудник российского представительства компании Microsoft (alexeif@microsoft.com))

Механизм Application Restart and Recovery

Использование механизма 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()

Флаг

Значение

Описание

RESTART_NO_CRASH

1

Не перезапускать процесс, если он был завершен в результате возникновения необработанного исключения

RESTART_NO_HANG

2

Не перезапускать процесс, если он был завершен в результате зависания приложения

RESTART_NO_PATCH

4

Не перезапускать процесс, если он был завершен в результате установки обновления ОС

RESTART_NO_REBOOT

8

Не перезапускать процесс, если он был завершен в результате перезапуска компьютера после установки обновления ОС

 

Таблица 2. Функции для расширенной поддержки механизма Application Restart and Recovery

Функция

Описание

ApplicationRecoveryFinished()

Указывает на то, что вызывающее приложение завершило восстановление данных

ApplicationRecoveryInProgress()

Указывает на то, что вызывающее приложение продолжает восстанавливать данные

GetApplicationRecoveryCallback()

Возвращает указатель на косвенно­вызываемую функцию, зарегистрированную для указанного процесса

GetApplicationRestartSettings()

Возвращает информацию о перезапуске приложения для указанного процесса

RegisterApplicationRecoveryCallback()

Регистрирует косвенно­вызываемую функцию, используемую для восстановления приложения

UnregisterApplicationRecoveryCallback()

Удаляет информацию о приложении из списка восстанавливаемых приложений

UnregisterApplicationRestart()

Удаляет информацию о приложении из списка перезагружаемых приложений

На уровне ядра операционной системы также существует ряд дополнительных функций, которые можно использовать для расширенной поддержки механизма Application Restart and Recovery (табл. 2).

Ключевой здесь является возможность регистрации косвенно­вызываемой функции — ApplicationRecoveryCallback, которая может использоваться для сохранения данных и состояния приложения в тех случаях, когда приложение зависает или возникает необрабатываемая критическая ошибка. После вызова зарегистрированной косвенно­вызываемой функции приложение должно периодически (интервал указывается при регистрации функции, по умолчанию — каждые 5 с) вызывать функцию ApplicationRecoveryInProgress(), указывая на то, что идет процесс сохранения данных, а после завершения сохранения — функцию ApplicationRecoveryFinished().

Помимо этого существует ряд функций, с помощью которых приложение может при необходимости принудительно завершать и перезапускать другие приложения (при наличии соответствующих прав).

К таким функциям относятся функции, перечисленные в табл. 3, — все они реализованы в библиотеке Rstrtmgr.dll, а их прототипы описаны в файле RestartManager.h.

 

Таблица 3. Функции для принудительного завершения и перезапуска приложений

Функция

Описание

RmAddFilter()

Изменяет действия, связанные с принудительным завершением работы приложений и их перезапуском

RmStartSession()

Стартует новую сессию Restart Manager. Поддерживается до 64 одновременных сессий

RmJoinSession()

Включает процесс, связанный с приложением, в уже существующую сессию Restart Manager

RmEndSession()

Завершает сессию Restart Manager

RmRegisterResources()

Регистрирует ресурсы — имена файлов, имена сервисов или структуры RM_UNIQUE_PROCESS в сессии Restart Manager

RmGetList()

Используется программами установки приложений для получения списка приложений, связанных
с зарегистрированными ресурсами, и их текущего статуса

RmGetFilterList()

Запрашивает статус изменений состояния принудительного завершения и перезапуска

RmShutdown()

Инициирует принудительное завершение приложения или процесса

RmRemoveFilter()

Удаляет предварительно внесенные изменения в действия, связанные с принудительным завершением работы приложений и их перезапуском

RmRestart()

Перезапускает приложения и сервисы, которые были принудительно завершены функцией RmShutdown() и зарегистрированы с помощью функции RegisterApplicationRestart()

RmCancelCurrentTask()

Отменяет действия функций RmGetList(), RmShutdown() и RmRestart()

Ниже показан пример консольного приложения, использующего механизм Restart Manager для принудительного завершения и перезапуска процесса — в данном примере применяется утилита Calculator.

/*

Базовый пример использования механизма Restart Manager

*/

 

#include <windows.h>

#include <restartmanager.h>

 

int _cdecl wmain()

{

DWORD dwErrCode = ERROR_SUCCESS;

DWORD dwSessionHandle = 0xFFFFFFFF;

 

//

// CCH_RM_SESSION_KEY: Число символов в названии сессии

//

WCHAR sessKey[CCH_RM_SESSION_KEY+1];

 

// Число регистрируемых файлов

DWORD dwFiles = 2;

 

//

// Регистрация двух файлов — для 32­разрядной и 64­разрядной версии

//

LPCWSTR rgsFiles[] = {L"C:\\Windows\\System32\\calc.exe",

L"C:\\Windows\\SysWow64\\calc.exe"};

 

UINT nRetry = 0;

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)

{

goto RM_CLEANUP;

}

//

// Регистрация файлов в Restart Manager

// Ниже регистрируются два исполняемых файла.

// Также возможна регистрация файлов, процессов и сервисов

//

dwErrCode = RmRegisterResources(dwSessionHandle,

dwFiles,

rgsFiles, // Файлы

0,

NULL, // Процессы

0,

NULL); // Сервисы

 

if (ERROR_SUCCESS != dwErrCode)

{

goto RM_CLEANUP;

}

 

//

// Получение списка приложений и сервисов

//

 

do

{

dwErrCode = RmGetList(dwSessionHandle,

&nProcInfoNeeded,

&nAffectedApps,

rgAffectedApps,

(LPDWORD) &dwRebootReasons);

if (ERROR_SUCCESS == dwErrCode)

{

//

// Успешное выполнение RmGetList()

//

break;

}

 

if (ERROR_MORE_DATA != dwErrCode)

{

//

// Неудачное выполнение RmGetList()

// с ошибкой, отличной от ERROR_MORE_DATA

//

goto RM_CLEANUP;

}

 

//

// Запрос дополнительных данных

//

nAffectedApps = nProcInfoNeeded;

 

if (NULL != rgAffectedApps)

{

delete []rgAffectedApps;

rgAffectedApps = NULL;

}

 

rgAffectedApps = new RM_PROCESS_INFO[nAffectedApps];

 

} while ((ERROR_MORE_DATA == dwErrCode) && (nRetry ++ < 3));

 

if (ERROR_SUCCESS != dwErrCode)

{

goto RM_CLEANUP;

}

 

if (RmRebootReasonNone != dwRebootReasons)

{

//

// Невозможно выполнить перезагрузку

// Очистка данных

//

goto RM_CLEANUP;

}

 

//

// Список rgAffectedApps содержит приложения и сервисы

// Число приложений и сервисов — в переменной nAffectedApps

//

// Основной код помещается здесь

 

//

// Завершение работы всех приложений и сервисов из списка

//

 

dwErrCode = RmShutdown(dwSessionHandle, 0, NULL);

 

if (ERROR_SUCCESS != dwErrCode)

{

goto RM_CLEANUP;

}

 

//

// Ресурсы освобождены — возможность обновления/замены

// исполняемого файла

//

// Основной код помещается здесь

 

 

//

// Перезапуск приложений и сервисов после обновления файлов

//

dwErrCode = RmRestart(dwSessionHandle, 0, NULL);

if (ERROR_SUCCESS != dwErrCode)

{

goto RM_CLEANUP;

}

 

RM_CLEANUP:

 

if (NULL != rgAffectedApps)

{

delete [] rgAffectedApps;

rgAffectedApps = NULL;

}

 

if (0xFFFFFFFF != dwSessionHandle)

{

//

// Очистка сессии Restart Manager

//

RmEndSession(dwSessionHandle);

dwSessionHandle = 0xFFFFFFFF;

}

 

return 0;

}

В следующем примере показано, как реализовать автоматический перезапуск приложения за счет регистрации в соответствующем сервисе Application Restart and Recovery:

#include <stdio.h>

#include <windows.h>

 

int

wmain (

int argc,

const wchar_t* argv[],

const wchar_t* envp[]

)

{

HRESULT hr = E_FAIL;

int i;

 

 

UNREFERENCED_PARAMETER (envp);

 

//

// Обычный запуск или перезапуск?

//

if (argc >= 2 &&

0 == _wcsicmp (argv[1], L"/restarted")) {

 

wprintf (L"Приложение было перезапущено.\n");

wprintf (L"Нажмите ENTER для выхода.\n");

getwc (stdin);

return 0;

}

 

//

// Обычный запуск

//

 

//

// Зарегистрировать приложение

//

hr = RegisterApplicationRestart (L»/restarted»,

0);

if (FAILED (hr)) {

wprintf (L"Ошибка RegisterApplicationRestart — код 0x%08X\n", hr);

return ­1;

}

 

wprintf (L"Успешная регистрация приложения.\n");

//

// Ждать как минимум 60 с и вызвать ошибку

//

wprintf (L"Ждем 62 с...");

 

for (i = 0; i < 62; ++i) {

wprintf (L" %d", i);

 

Sleep (1000);

}

 

wprintf (L"\n");

 

//

// Вызвать ошибку — запись в нулевой указатель

// Приложение будет перезапущено ядром ОС

//

wprintf (L"Crashing the application…\n");

fflush (stdout);

 

*((int*)NULL) = 0;

 

 

return 0;

}

И еще один пример использования механизма Application Restart and Recovery — возможность сохранения данных и их восстановления после перезапуска приложения:

#include <stdio.h>

 

#include <windows.h>

 

//

// Информация о состоянии, сохраняемая на диске

//

typedef struct _STATE_BLOCK {

char RandomNumber[16];

} STATE_BLOCK, *PSTATE_BLOCK;

 

static STATE_BLOCK g_StateBlock;

 

//

// Функция­делегат, вызываемая WER

//

static

DWORD WINAPI

MyRecoveryCallback (

PVOID pvParameter

)

{

HRESULT hr = E_FAIL;

BOOL rc;

HANDLE FileHandle;

DWORD BytesWritten;

BOOL RecoveryCancelled;

PSTATE_BLOCK StateBlock = (PSTATE_BLOCK) pvParameter;

 

//

// Уведомление WER о восстановлении

//

hr = ApplicationRecoveryInProgress (&RecoveryCancelled);

if (FAILED (hr)) {

//

// Ошибка — прервать восстановление

//

ApplicationRecoveryFinished (FALSE);

return 0;

}

 

if (RecoveryCancelled) {

//

// Пользователь отменил восстановление

//

return 0;

}

 

//

// Восстановление. Если процесс займет больше времени, чем

// RECOVERY_DEFAULT_PING_INTERVAL, нужно вызвать функцию

// ApplicationRecoveryInProgress, чтобы сообщить WER

// о том, что восстановление еще не завершено

//

FileHandle = CreateFile (L"recovered_data.txt",

GENERIC_WRITE,

0,

NULL,

CREATE_ALWAYS,

0,

NULL);

 

if (INVALID_HANDLE_VALUE != FileHandle) {

//

// Записать данные в файл

//

rc = WriteFile (FileHandle, StateBlock, sizeof (STATE_BLOCK), &BytesWritten, NULL);

if (!rc) {

//

// Ошибка записи в файл — сообщить WER

// о завершении восстановления

//

ApplicationRecoveryFinished (FALSE);

return 0;

}

CloseHandle (FileHandle);

 

//

// Успешное завершение восстановления — сообщить WER

//

ApplicationRecoveryFinished (TRUE);

 

return 0;

}

else {

//

// Ошибка открытия файла. Сообщить WER о завершении

//

ApplicationRecoveryFinished (FALSE);

 

return 0;

}

}

 

int

wmain (

int argc,

const wchar_t* argv[],

const wchar_t* envp[]

)

{

HRESULT hr = E_FAIL;

 

 

UNREFERENCED_PARAMETER (argc);

UNREFERENCED_PARAMETER (argv);

UNREFERENCED_PARAMETER (envp);

 

//

// Регистрация приложения

//

hr = RegisterApplicationRecoveryCallback (MyRecoveryCallback,

&g_StateBlock, RECOVERY_DEFAULT_PING_INTERVAL, 0);

 

if (FAILED (hr)) {

wprintf (L"Ошибка RegisterApplicationRecoveryCallback.

Код 0x%08X\n", hr);

return ­1;

}

 

wprintf (L"Успешная регистрация.\n");

 

//

// Заполним буфер случайными данными

//

sprintf_s (g_StateBlock.RandomNumber,

sizeof (g_StateBlock.RandomNumber), «%d», rand ());

//

// Вызвать ошибку — запись в нулевой указатель

// Приложение будет перезапущено ядром ОС

//

wprintf (L"Вызываем ошибку в приложении...\n");

fflush (stdout);

 

*((int*)NULL) = 0;

 

return 0;

}

 

Сервисы приложений для управляемого кода

Сервисы приложений для управляемого кода

В начало В начало

Использование механизма Application Restart and Recovery из управляемого кода

СС помощью Windows API Code Pack for Microsoft .NET Framework можно применять ряд перечисленных выше функций в приложениях, написанных на управляемом коде — C# или Visual Basic .NET.

Пространство имен Microsoft.WindowsAPICodePack.ApplicationServices содержит ряд классов и структур, позволяющих обращаться к функциям Restart Manager из управляемого кода (см. рисунок).

Класс ApplicationRestartRecoveryManager обеспечивает доступ к функциям механизма Application Restart and Recovery, класс RecoveryData содержит функцию­делегат (RecoveryCallback) и набор свойств для приложения, класс RecoverySettings содержит набор методов и свойств для управления процессом восстановления после перезагрузки, а класс RestartSettings задает опции для автоматической перезагрузки.

***

В следующей статье данного цикла мы расскажем о механизме Windows Error Reporting.

В начало В начало

КомпьютерПресс 06'2011

Наш канал на Youtube

1999 1 2 3 4 5 6 7 8 9 10 11 12
2000 1 2 3 4 5 6 7 8 9 10 11 12
2001 1 2 3 4 5 6 7 8 9 10 11 12
2002 1 2 3 4 5 6 7 8 9 10 11 12
2003 1 2 3 4 5 6 7 8 9 10 11 12
2004 1 2 3 4 5 6 7 8 9 10 11 12
2005 1 2 3 4 5 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
Популярные статьи
КомпьютерПресс использует