Использование диспетчера перезапуска с дополнительным установщиком
В следующей процедуре описывается использование диспетчера перезапуска с основным и вторичным установщиками.
При использовании основного и дополнительного установщиков основной установщик управляет пользовательским интерфейсом.
Использование диспетчера перезапуска с основным и дополнительным установщиками
Основной установщик вызывает функцию RmStartSession , чтобы запустить сеанс Restart Manager и получить дескриптор сеанса и ключ.
Основной установщик запускает дополнительный установщик или связывается с ним и предоставляет ему ключ сеанса, полученный на предыдущем шаге.
Дополнительный установщик вызывает функцию RmJoinSession с ключом сеанса для присоединения к сеансу Диспетчера перезапуска. Дополнительный установщик может присоединиться к сеансу, запущенному основным установщиком, только если оба установщика выполняются в одном контексте пользователя.
Основной и вторичный установщики вызывают функцию RmRegisterResources для регистрации ресурсов. Диспетчер перезапуска может использовать только зарегистрированные ресурсы, чтобы определить, какие приложения и службы необходимо завершить работу и перезапустить. Все ресурсы, на которые может повлиять установка, должны быть зарегистрированы в сеансе. Ресурсы можно определить по имени файла, короткому имени службы или структуре RM_UNIQUE_PROCESS .
Основной установщик вызывает функцию RmGetList для получения массива структур RM_PROCESS_INFO , включаемых в список всех приложений и служб, которые должны быть завершены и перезапущены.
Если значение параметра lpdwRebootReason , возвращаемое функцией RmGetList , не равно нулю, диспетчер перезапуска не может освободить зарегистрированный ресурс после завершения работы приложения или службы. В этом случае для продолжения установки требуется завершение работы и перезагрузка системы. Основной установщик должен запрашивать у пользователя действие, останавливать программы или службы или планировать завершение работы и перезапуск системы.
Если значение параметра lpdwRebootReason , возвращаемого функцией RmGetList , равно нулю, установщик должен вызвать функцию RmShutdown . Это завершает работу служб и приложений, использующих зарегистрированные ресурсы. Затем основной и вторичный установщики должны выполнить изменения системы, необходимые для завершения установки. Наконец, основной установщик должен вызвать функцию RmRestart , чтобы диспетчер перезапуска смог перезапустить приложения, которые он завершил работу и которые были зарегистрированы для перезапуска.
Основной и дополнительный установщик вызывают функцию RmEndSession , чтобы закрыть сеанс диспетчера перезапуска.
В следующем фрагменте кода показан пример запуска основного установщика и использования сеанса Диспетчера перезапуска. Для этого примера требуется Windows 7 или Windows Server 2008 R2. В Windows Vista или Windows Server 2008 приложение Calculator завершает работу, но не перезапускается. В этом примере показано, как основной установщик может использовать диспетчер перезапуска для завершения работы и перезапуска процесса. В примере предполагается, что калькулятор уже запущен перед запуском сеанса диспетчера перезапуска.
#include <windows.h>
#include <restartmanager.h>
#pragma comment(lib, "rstrtmgr.lib")
int _cdecl wmain()
{
DWORD dwErrCode = ERROR_SUCCESS;
DWORD dwSessionHandle = 0xFFFFFFFF; // Invalid handle value
//
// CCH_RM_SESSION_KEY: Character count of the
// Text-Encoded session key,defined in RestartManager.h
//
WCHAR sessKey[CCH_RM_SESSION_KEY+1];
// Number of calc files to be registered.
DWORD dwFiles = 2;
//
// NOTE:We register two calc executable files.
// The second one is for the redirection of 32 bit calc on
// 64 bit machines. Even if you are using a 32 bit machine,
// you don't need to comment out the second line.
//
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;
//
// Start a Restart Manager Session
//
dwErrCode = RmStartSession(&dwSessionHandle, 0, sessKey);
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
//
// Register items with Restart Manager
//
// NOTE: we only register two calc executable files
// in this sample. You can register files, processes
// (in the form of process ID), and services (in the
// form of service short names) with Restart Manager.
//
dwErrCode = RmRegisterResources(dwSessionHandle,
dwFiles,
rgsFiles, // Files
0,
NULL, // Processes
0,
NULL); // Services
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
//
// Obtain the list of affected applications/services.
//
// NOTE: Restart Manager returns the results into the
// buffer allocated by the caller. The first call to
// RmGetList() will return the size of the buffer
// (i.e. nProcInfoNeeded) the caller needs to allocate.
// The caller then needs to allocate the buffer
// (i.e. rgAffectedApps) and make another RmGetList()
// call to ask Restart Manager to write the results
// into the buffer. However, since Restart Manager
// refreshes the list every time RmGetList()is called,
// it is possible that the size returned by the first
// RmGetList()call is not sufficient to hold the results
// discovered by the second RmGetList() call. Therefore,
// it is recommended that the caller follows the
// following practice to handle this race condition:
//
// Use a loop to call RmGetList() in case the buffer
// allocated according to the size returned in previous
// call is not enough.
//
// In this example, we use a do-while loop trying to make
// 3 RmGetList() calls (including the first attempt to get
// buffer size) and if we still cannot succeed, we give up.
//
do
{
dwErrCode = RmGetList(dwSessionHandle,
&nProcInfoNeeded,
&nAffectedApps,
rgAffectedApps,
(LPDWORD) &dwRebootReasons);
if (ERROR_SUCCESS == dwErrCode)
{
//
// RmGetList() succeeded
//
break;
}
if (ERROR_MORE_DATA != dwErrCode)
{
//
// RmGetList() failed, with errors
// other than ERROR_MORE_DATA
//
goto RM_CLEANUP;
}
//
// RmGetList() is asking for more data
//
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)
{
//
// Restart Manager cannot mitigate a reboot.
// We goes to the clean up. The caller may want
// to add additional code to handle this scenario.
//
goto RM_CLEANUP;
}
//
// Now rgAffectedApps contains the affected applications
// and services. The number of applications and services
// returned is nAffectedApps. The result of RmGetList can
// be interpreted by the user to determine subsequent
// action (e.g. ask user's permission to shutdown).
//
// CALLER CODE GOES HERE...
//
// Shut down all running instances of affected
// applications and services.
//
dwErrCode = RmShutdown(dwSessionHandle, 0, NULL);
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
//
// An installer can now replace or update
// the calc executable file.
//
// CALLER CODE GOES HERE...
//
// Restart applications and services, after the
// files have been replaced or updated.
//
dwErrCode = RmRestart(dwSessionHandle, 0, NULL);
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
RM_CLEANUP:
if (NULL != rgAffectedApps)
{
delete [] rgAffectedApps;
rgAffectedApps = NULL;
}
if (0xFFFFFFFF != dwSessionHandle)
{
//
// Clean up the Restart Manager session.
//
RmEndSession(dwSessionHandle);
dwSessionHandle = 0xFFFFFFFF;
}
return 0;
}
В следующем фрагменте кода показан пример присоединения дополнительного установщика к существующему сеансу Restart Manager. Для этого примера требуется Windows Vista или Windows Server 2008. Дополнительный установщик получает ключ сеанса от основного установщика и использует его для присоединения к сеансу.
#include <windows.h>
#include <restartmanager.h>
#pragma comment(lib, "rstrtmgr.lib")
int _cdecl wmain()
{
DWORD dwErrCode = ERROR_SUCCESS;
DWORD dwSessionHandle = 0xFFFFFFFF; // Invalid handle value
//
// CCH_RM_SESSION_KEY: Character count of the
// Text-Encoded session key, defined in RestartManager.h
//
WCHAR sessKey[CCH_RM_SESSION_KEY+1];
// Number of files to be registered.
DWORD dwFiles = 1;
//
// We register oleaut32.dll with Restart Manager.
//
LPCWSTR rgsFiles[] =
{L"C:\\Windows\\System32\\oleaut32.dll"};
//
// Secondary installer needs to obtain the session key from
// the primary installer and uses it to join the session.
//
// CALLER CODE GOES HERE ...
//
// We assume the session key obtained is stored in sessKey
dwErrCode = RmJoinSession(&dwSessionHandle, sessKey);
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
//
// Register items with Restart Manager
//
// NOTE: In Vista, the subordinate is only allowed to
// register resources and cannot perform any other
// getlist, shutdown, restart or filter operations.
//
dwErrCode = RmRegisterResources(dwSessionHandle,
dwFiles,
rgsFiles, // Files
0,
NULL, // Processes
0,
NULL); // Services
if (ERROR_SUCCESS != dwErrCode)
{
goto RM_CLEANUP;
}
RM_CLEANUP:
if (0xFFFFFFFF != dwSessionHandle)
{
//
// The subordinate leaves the conductor's session
// by calling RmEndSession(). The session itself
// won't be destroyed until the primary installer
// calls RmEndSession()
//
RmEndSession(dwSessionHandle);
dwSessionHandle = 0xFFFFFFFF;
}
return 0;
}