Restart Manager(以下簡稱RM)可以減少或避免安裝或更新程序所需要的系統重啟次數。安裝(或更新)過程中需要重啟的主要原因是需要更新的某些文件當前正被一些其它程序或服務所使用。RM允許除關鍵系統服務(Critical System Services)之外的所有程序(或服務)關閉和重啟。這將釋放正在使用的文件從而使安裝得以完成。
RM DLL導出了一組可供安裝包使用的C接口。安裝包可以向RM注冊安裝過程中需要被替換的文件列表,隨后,安裝包可以通過RM來判斷其中哪些文件正在使用;RM可以關閉並重啟那些占用了這些文件的程序;安裝包可以根據被占用的文件,進程ID,或Windows服務的短名稱來指導RM關閉和重啟應用程序(或服務)。
RM自Vista開始提供,Windows Installer V4會自動使用RM,自定義Installer也可以調用RM API來使用其功能。在不可避免需要重啟的情況下,Installer可以借助RM來規划何時重啟以最大限度減少對用戶工作流的干擾。
對占用文件的程序,RM以下列順序關閉它們,並在完成更新后,以相反的順序重啟它們:
1. GUI應用程序
2. Console應用程序
3. Windows服務
4. Windows Explorer
RM只有得到調用方允許后才會關閉程序(或服務)。不支持跨會話進行關閉操作。
對於使用Windows Installer V4的安裝包,如果它是交互式的,則其用戶接口應包括MsiRMFilesInUse對話框。
1. 在自定義安裝包中使用RM API
所有通過RM API執行的操作都必須與一個session關聯。在一個用戶會話中,最多可以同時開啟64個RM session。主安裝包(Primary Installer)負責啟動和停止RM session。
必要的情況下,若干個從安裝包(Secondary Installer)可以加入RM Session並與主安裝包同時運行(在同一進程或不同進程中)。加入RM Session需要使用其session key。
交互式安裝包的用戶界面應包括一個MsiRMFilesInUse對話框 —— 用於請求用戶關閉應用程序或服務。
安裝包不能在調用RM API前禁用文件系統重定向,這意味着在64位Windows上運行的32位安裝包不能注冊“%windir\system32”目錄中的文件。
1.1 在主安裝包中使用RM
當在單個安裝包中使用RM時,該安裝包也就是主安裝包。
1. 調用RmStartSession啟動一個RM session,得到session handle和key。
2. 調用RmRegisterResources注冊資源。RM只能通過注冊的資源來判斷哪些程序和服務需要被重啟。資源可以是文件名、服務的短名稱,或一個RM_UNIQUE_PROCESS結構。
3. 調用RmGetList獲得一個RM_PROCESS_INFO數組,其中包含了所有需要被重啟的進程和服務。
如果RmGetList返回的lpdwRebootReason不為0,則表示RM無法通過重啟來釋放所注冊的資源。在這種情況下,則需要重啟OS來完成安裝。
如果RmGetList返回的lpdwRebootReason為0,則可以通過調用RmShutdown來關閉占用資源的程序和服務,然后安裝包可以進行安裝操作,最后,調用RmRestart來重啟被關閉的程序。
4. 可以通過RmAddFilter來防止某些程序(或服務)被RM關閉,這稱為添加一個過濾器。RmRemoveFilter可以移除一個過濾器,RmGetFilterList則可以獲得當前的過濾器列表。
5. 調用RmEndSession關閉RM session。
示例:
DWORD dVal = 0;
DWORD dwSessionHandle = (DWORD) -1;
WCHAR sessKey[CCH_RM_SESSION_KEY+1];
UINT nProcInfo = 100;
UINT nProcInfoNeeded;
UINT nAffectedApps = 0;
RM_PROCESS_INFO rgAffectedApps[100]; //Size depends on # of entries found by RmGetList
DWORD lpdwRebootReason = 0;
DWORD nServices = 1;
LPCWSTR rgsServices[] = { L"iisadmin" };
DWORD nProcs = 0;
DWORD nFiles = 1;
LPCWSTR rgsFiles[] = { L"c:\\windows\\system32\\oleaut32.dll" };
// Starting Session
dVal = RmStartSession( &dwSessionHandle, 0, sessKey );
if (dVal != ERROR_SUCCESS)
goto RM_END;
// Register items
dVal = RmRegisterResources(
dwSessionHandle,
nFiles, rgsFiles, // Files
nProcs, NULL, // Processes
nServices, rgsServices ); // Services
if (dVal != ERROR_SUCCESS)
goto RM_END;
// Getting affected apps
dVal = RmGetList(
dwSessionHandle,
&nProcInfoNeeded,
&nAffectedApps, rgAffectedApps, &lpdwRebootReason );
if (dVal != ERROR_SUCCESS)
goto RM_END;
//Results of RmGetList can be presented & interpreted
//by the user for determining subsequent action.
// Shut down apps
dVal = RmShutdown( dwSessionHandle, 0, NULL );
if (dVal != ERROR_SUCCESS)
goto RM_END;
//An installer can now replace the files.
// Restart apps
dVal = RmRestart( dwSessionHandle, 0, NULL );
if (dVal != ERROR_SUCCESS)
goto RM_END;
RM_END:
if (rgAffectedApps)
{
delete [] rgAffectedApps;
rgAffectedApps = NULL;
}
if (dwSessionHandle != -1)
{
// Clean up session
dVal = RmEndSession( dwSessionHandle );
dwSessionHandle = -1;
}
1.2 在從安裝包中使用RM
1. 想辦法從主安裝包中獲取RM session key,調用RMJoinSession來加入session。主安裝包和從安裝包必須運行在相同的用戶上下文中。
2. 使用RmRegisterResources注冊資源;使用RmGetList可以獲取占用資源的程序列表;
3. 可以調用RmShutdown(存疑!如果能夠調用的話,那會不會多次調用RmShutdown的可能?)。
4. 主安裝包和從安裝包調用RmEndSession(存疑,不知道是指任何一方調用即可還是雙方都需要調用一次)。
示例:
DWORD dVal = 0;
DWORD dwSessionHandle = (DWORD) -1;
WCHAR sessKey[CCH_RM_SESSION_KEY+1]; //Primary installer session key.
DWORD nServices = 1;
LPCWSTR rgsServices[] = { L"iisadmin" };
DWORD nProcs = 0;
DWORD nFiles = 1;
LPCWSTR rgsFiles[] = { L"c:\\windows\\system32\\oleaut32.dll" };
// Secondary installer obtains the session key from the
// primary installer & uses it to join the session.
// Joining Session
dVal = RmJoinSession( &dwSessionHandle, sessKey );
if (dVal != ERROR_SUCCESS)
goto RMSUB_END;
// Register items. The Secondary installer is only allowed to register resources
// and cannot perform other Restart Manager operations.
dVal = RmRegisterResources(
dwSessionHandle,
nFiles, rgsFiles, // Files
nProcs, NULL, // Processes
nServices, rgsServices ); // Services
if (dVal != ERROR_SUCCESS)
goto RMSUB_END;
RMSUB_END:
if (dwSessionHandle != -1)
{
// Clean up session
dVal = RmEndSession( dwSessionHandle );
dwSessionHandle = -1;
}
來自:ms-help://MS.MSDNQTR.v90.chs/rstmgr/rstmgr/using_restart_manager.htm
