Graceful Restart of Windows Explorer via the Restart Manager API
Graceful termination of the Windows shell process requires more than brute-force methods like TerminateProcess or taskkill. The Restart Manager API provides a cooperative mechanism that preserves user session state and allows clean resource reclamation while minimizing the need for system reboots.
Restart Manager Fundamentals
The Restart Manager (RM) infrastructure enables installers and system utilities to temporarily suspend applications that lock files targeted for update, then restore them afterward. Unlike forced termination, RM attempts to persist window positions, unsaved document state, and configuration data where supported.
Key functions include:
RmStartSession/RmEndSession: Lifecycle managementRmRegisterResources: Declaring target processes viaRM_UNIQUE_PROCESSstructuresRmShutdown: Cooperative termination with optional force flagRmRestart: State-aware process resurrection
Identifying Target Processes
The API requires both Process ID and creation timestamp to uniquely identify processes. This prevents race conditions where a PID might be recycled between enumeration and termination.
#include <windows.h>
#include <tlhelp32.h>
#include <vector>
#include <restartmanager.h>
#pragma comment(lib, "rstrtmgr.lib")
struct ProcessIdentity {
DWORD pid;
FILETIME startTime;
};
bool EnumerateShellProcesses(std::vector<ProcessIdentity>& buffer) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) return false;
PROCESSENTRY32W entry = { sizeof(entry) };
BOOL hasMore = Process32FirstW(snapshot, &entry);
while (hasMore) {
if (_wcsicmp(entry.szExeFile, L"explorer.exe") == 0) {
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, entry.th32ProcessID);
if (process) {
FILETIME creation, exit, kernel, user;
if (GetProcessTimes(process, &creation, &exit, &kernel, &user)) {
buffer.push_back({entry.th32ProcessID, creation});
}
CloseHandle(process);
}
}
hasMore = Process32NextW(snapshot, &entry);
}
CloseHandle(snapshot);
return !buffer.empty();
}
Session Management Constraints
The Restart Manager imposes strict architectural limits:
-
Global Session Limit: The system permits maximum 64 concurrent RM sessions. Attempting to exceed this causes
RmStartSessionto fail withERROR_MAX_SESSIONS_REACHED(1058). -
Handle Persistence: Sesion handles persist until explicitly closed via
RmEndSessionor the originating process terminates. Leaked handles reduce system capacity until reboot. -
Restart Precondisions:
RmRestartsucceeds only when invoked on a session whereRmShutdownpreviously executed successfully. The same session handle must be used for both operations; restarting from a new session is impossible. -
Resource Binding: Sessions must register specific resources (files, processes, or services) before shutdown. Empty sessions cannot trigger restarts.
Complete Implementation
The following implementation demonstrates the complete workflow: establishing a session, registering discovered Explorer instances, executing graceful shutdown, delaying briefly to ensure resource release, then restoring the shell.
#include <iostream>
#include <vector>
#include <windows.h>
#include <restartmanager.h>
#include <tlhelp32.h>
#pragma comment(lib, "rstrtmgr.lib")
struct ProcInfo {
DWORD id;
FILETIME created;
};
void StatusCallback(UINT percent) {
std::wcout << L"Operation progress: " << percent << L"%\n";
}
bool CollectTargets(std::vector<ProcInfo>& targets) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return false;
PROCESSENTRY32W pe = { sizeof(pe) };
for (BOOL ok = Process32FirstW(snap, &pe); ok; ok = Process32NextW(snap, &pe)) {
if (_wcsicmp(pe.szExeFile, L"explorer.exe")) continue;
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID);
if (!h) continue;
FILETIME create, exit, kernel, user;
if (GetProcessTimes(h, &create, &exit, &kernel, &user)) {
targets.push_back({pe.th32ProcessID, create});
}
CloseHandle(h);
}
CloseHandle(snap);
return !targets.empty();
}
int wmain() {
DWORD session = 0;
WCHAR sessionKey[CCH_RM_SESSION_KEY + 1] = {};
if (RmStartSession(&session, 0, sessionKey) != ERROR_SUCCESS) {
std::wcerr << L"Session creation failed\n";
return 1;
}
std::vector<ProcInfo> explorers;
if (!CollectTargets(explorers)) {
std::wcerr << L"No target processes found\n";
RmEndSession(session);
return 1;
}
std::vector<RM_UNIQUE_PROCESS> rmProc;
rmProc.reserve(explorers.size());
for (const auto& e : explorers) {
rmProc.push_back({e.id, e.created});
}
if (RmRegisterResources(session, 0, nullptr, static_cast<UINT>(rmProc.size()),
rmProc.data(), 0, nullptr) != ERROR_SUCCESS) {
std::wcerr << L"Registration failed\n";
RmEndSession(session);
return 1;
}
std::wcout << L"Initiating shutdown...\n";
DWORD result = RmShutdown(session, RmForceShutdown, StatusCallback);
if (result == ERROR_SUCCESS) {
Sleep(3000);
std::wcout << L"Triggering restart...\n";
if (RmRestart(session, 0, StatusCallback) != ERROR_SUCCESS) {
std::wcerr << L"Restart operation failed\n";
}
} else {
std::wcerr << L"Shutdown failed with code: " << result << L"\n";
}
RmEndSession(session);
return 0;
}
State restoration depends on the application's support for the Restart Manager protocol. While Explorer generally restores desktop configuration, modifications to icon layouts or taskbar settings made during the shtudown window may not persist. The API uses internal window record serialization to restore previous layouts when possible.