セカンダリ インストーラーでの再起動マネージャーの使用
次の手順では、プライマリ インストーラーとセカンダリ インストーラーでの再起動マネージャーの使用について説明します。
プライマリ インストーラーとセカンダリ インストーラーを使用する場合、プライマリ インストーラーによってユーザー インターフェイスが制御されます。
プライマリ インストーラーとセカンダリ インストーラーで Restart Manager を使用するには
プライマリ インストーラーは RmStartSession 関数を呼び出して再起動マネージャー セッションを開始し、セッション ハンドルとキーを取得します。
プライマリ インストーラーは、セカンダリ インストーラーを起動するか、またはセカンダリ インストーラーに問い合わせ、前の手順で取得したセッション キーを提供します。
セカンダリ インストーラーは、セッション キーを使用して RmJoinSession 関数を呼び出して、Restart Manager セッションに参加します。 セカンダリ インストーラーは、両方のインストーラーが同じユーザー コンテキストで実行されている場合にのみ、プライマリ インストーラーによって開始されるセッションに参加できます。
プライマリ インストーラーとセカンダリ インストーラーは 、RmRegisterResources 関数を呼び出してリソースを登録します。 再起動マネージャーは、登録されているリソースのみを使用して、シャットダウンと再起動が必要なアプリケーションとサービスを決定できます。 インストールの影響を受ける可能性のあるすべてのリソースをセッションに登録する必要があります。 リソースは、filename、service short name、または 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 では、電卓アプリケーションはシャットダウンされますが、再起動されません。 この例では、プライマリ インストーラーが再起動マネージャーを使用してプロセスをシャットダウンおよび再起動する方法を示します。 この例では、再起動マネージャー セッションを開始する前に、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;
}
次のコード スニペットは、セカンダリ インストーラーを既存の再起動マネージャー セッションに参加させる例を示しています。 この例では、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;
}