Używanie Menedżera ponownego uruchamiania z instalatorem pomocniczym
Poniższa procedura opisuje użycie Menedżera ponownego uruchamiania z instalatorami podstawowymi i pomocniczymi.
W przypadku korzystania z instalatorów podstawowych i pomocniczych podstawowy instalator kontroluje interfejs użytkownika.
Aby użyć Menedżera ponownego uruchamiania z instalatorami głównymi i drugorzędnymi
Instalator podstawowy wywołuje funkcję RmStartSession, aby rozpocząć sesję Menedżera ponownego uruchamiania oraz uzyskać uchwyt i klucz sesji.
Instalator podstawowy uruchamia instalatora pomocniczego lub kontaktuje się z nim i udostępnia klucz sesji uzyskany w poprzednim kroku.
Instalator pomocniczy wywołuje funkcję RmJoinSession z kluczem sesji, aby dołączyć do sesji Menedżera restartu. Instalator pomocniczy może dołączyć do sesji, która jest uruchamiana przez instalatora podstawowego tylko wtedy, gdy oba instalatory działają w tym samym kontekście użytkownika.
Instalatory podstawowe i pomocnicze wywołają funkcję RmRegisterResources w celu zarejestrowania zasobów. Menedżer ponownego uruchamiania może używać tylko zarejestrowanych zasobów, aby określić, które aplikacje i usługi należy zamknąć i uruchomić ponownie. Wszystkie zasoby, na które może mieć wpływ instalacja, należy zarejestrować w sesji. Zasoby można zidentyfikować według nazwy pliku, krótkiej nazwy usługi lub struktury RM_UNIQUE_PROCESS.
Instalator podstawowy wywołuje funkcję RmGetList, aby uzyskać tablicę struktur RM_PROCESS_INFO, które wyświetlają listę wszystkich aplikacji i usług, które muszą zostać zamknięte i uruchomione ponownie.
Jeśli wartość parametru lpdwRebootReason zwracanego przez funkcję RmGetList jest niezerowa, Menedżer ponownego uruchamiania nie może zwolnić zarejestrowanego zasobu przez zamknięcie aplikacji lub usługi. W takim przypadku wymagane jest zamknięcie systemu i ponowne uruchomienie, aby kontynuować instalację. Instalator podstawowy powinien monitować użytkownika o akcję, zatrzymać programy lub usługi albo zaplanować zamknięcie i ponowne uruchomienie systemu.
Jeśli wartość parametru lpdwRebootReason zwracana przez funkcję RmGetList wynosi zero, instalator powinien wywołać funkcję RmShutdown. Spowoduje to zamknięcie usług i aplikacji korzystających z zarejestrowanych zasobów. Instalatory podstawowe i pomocnicze powinny następnie przeprowadzać modyfikacje systemowe wymagane do ukończenia instalacji. Na koniec podstawowy instalator powinien wywołać funkcję RmRestart, aby Menedżer ponownego uruchamiania mógł ponownie uruchomić aplikacje, które zostały zamknięte i które zostały zarejestrowane na potrzeby ponownego uruchomienia.
Instalator podstawowy i pomocniczy wywołuje funkcję RmEndSession, aby zamknąć sesję Menedżera ponownego uruchamiania.
Poniższy fragment kodu przedstawia przykład uruchamiania podstawowego instalatora i korzystania z sesji Menedżera ponownego uruchamiania. Przykład wymaga systemu Windows 7 lub Windows Server 2008 R2. W systemie Windows Vista lub Windows Server 2008 aplikacja Kalkulator jest zamykana, ale nie uruchamia się ponownie. W tym przykładzie pokazano, jak podstawowy instalator może użyć Menedżera ponownego uruchamiania w celu zamknięcia i ponownego uruchomienia procesu. W przykładzie przyjęto założenie, że aplikacja Calculator jest już uruchomiona przed rozpoczęciem sesji Menedżera ponownego uruchamiania.
#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;
}
Poniższy fragment kodu przedstawia przykład dołączenia instalatora pomocniczego do istniejącej sesji Menedżera ponownego uruchamiania. Przykład wymaga systemu Windows Vista lub Windows Server 2008. Instalator pomocniczy uzyskuje klucz sesji z podstawowego instalatora i używa go do dołączenia do sesji.
#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;
}