보조 설치 관리자에서 다시 시작 관리자 사용
다음 절차에서는 기본 및 보조 설치 관리자와 함께 Restart Manager를 사용하는 것을 설명합니다.
기본 및 보조 설치 관리자를 사용하는 경우 기본 설치 관리자는 사용자 인터페이스를 제어합니다.
기본 및 보조 설치 관리자에서 다시 시작 관리자를 사용하려면
기본 설치 관리자는 RmStartSession 함수를 호출하여 다시 시작 관리자 세션을 시작하고 세션 핸들 및 키를 가져옵니다.
기본 설치 관리자는 보조 설치 관리자를 시작하거나 연결하여 이전 단계에서 가져온 세션 키를 제공합니다.
보조 설치 관리자는 세션 키를 사용하여 RmJoinSession 함수를 호출하여 다시 시작 관리자 세션에 참가합니다. 보조 설치 관리자는 두 설치 관리자가 동일한 사용자 컨텍스트에서 실행되는 경우에만 주 설치 관리자가 시작하는 세션에 참가할 수 있습니다.
기본 및 보조 설치 관리자는 RmRegisterResources 함수를 호출하여 리소스를 등록합니다. Restart Manager는 등록된 리소스만 사용하여 종료하고 다시 시작해야 하는 애플리케이션 및 서비스를 결정할 수 있습니다. 설치의 영향을 받을 수 있는 모든 리소스를 세션에 등록해야 합니다. 리소스는 파일 이름, 서비스 짧은 이름 또는 RM_UNIQUE_PROCESS 구조로 식별할 수 있습니다.
기본 설치 관리자는 RmGetList 함수를 호출하여 종료하고 다시 시작해야 하는 모든 애플리케이션 및 서비스를 나열하는 RM_PROCESS_INFO 구조의 배열을 가져옵니다.
RmGetList 함수에서 반환하는 lpdwRebootReason 매개 변수 값이 0이 아닌 경우 다시 시작 관리자는 애플리케이션 또는 서비스를 종료하여 등록된 리소스를 해제할 수 없습니다. 이 경우 설치를 계속하려면 시스템 종료 및 다시 시작이 필요합니다. 기본 설치 관리자는 사용자에게 작업을 요청하거나, 프로그램 또는 서비스를 중지하거나, 시스템 종료 및 다시 시작을 예약해야 합니다.
RmGetList 함수에서 반환하는 lpdwRebootReason 매개 변수 값이 0이면 설치 관리자가 RmShutdown 함수를 호출해야 합니다. 그러면 등록된 리소스를 사용하는 서비스 및 애플리케이션이 종료됩니다. 그런 다음 기본 및 보조 설치 관리자는 설치를 완료하는 데 필요한 시스템 수정을 수행해야 합니다. 마지막으로, 기본 설치 관리자는 RmRestart 함수를 호출하여 다시 시작 관리자가 종료된 애플리케이션을 다시 시작하고 다시 시작을 위해 등록된 애플리케이션을 다시 시작할 수 있도록 해야 합니다.
기본 및 보조 설치 관리자는 RmEndSession 함수를 호출하여 다시 시작 관리자 세션을 닫습니다.
다음 코드 조각은 다시 시작 관리자 세션을 시작하고 사용하는 기본 설치 관리자의 예를 보여줍니다. 이 예제에는 Windows 7 또는 Windows Server 2008 R2가 필요합니다. Windows Vista 또는 Windows Server 2008에서는 계산기 애플리케이션이 종료되지만 다시 시작되지는 않습니다. 이 예제에서는 기본 설치 관리자가 Restart Manager를 사용하여 프로세스를 종료하고 다시 시작하는 방법을 보여줍니다. 이 예제에서는 다시 시작 관리자 세션을 시작하기 전에 계산기가 이미 실행 중이라고 가정합니다.
#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;
}
다음 코드 조각은 보조 설치 관리자를 기존 다시 시작 관리자 세션에 조인하는 예제를 보여 주는 코드 조각입니다. 이 예제에는 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;
}