Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Graceful Restart of Windows Explorer via the Restart Manager API

Tools 1

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 management
  • RmRegisterResources: Declaring target processes via RM_UNIQUE_PROCESS structures
  • RmShutdown: Cooperative termination with optional force flag
  • RmRestart: 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:

  1. Global Session Limit: The system permits maximum 64 concurrent RM sessions. Attempting to exceed this causes RmStartSession to fail with ERROR_MAX_SESSIONS_REACHED (1058).

  2. Handle Persistence: Sesion handles persist until explicitly closed via RmEndSession or the originating process terminates. Leaked handles reduce system capacity until reboot.

  3. Restart Precondisions: RmRestart succeeds only when invoked on a session where RmShutdown previously executed successfully. The same session handle must be used for both operations; restarting from a new session is impossible.

  4. 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.

Related Articles

Efficient Usage of HTTP Client in IntelliJ IDEA

IntelliJ IDEA incorporates a versatile HTTP client tool, enabling developres to interact with RESTful services and APIs effectively with in the editor. This functionality streamlines workflows, replac...

Installing CocoaPods on macOS Catalina (10.15) Using a User-Managed Ruby

System Ruby on macOS 10.15 frequently fails to build native gems required by CocoaPods (for example, ffi), leading to errors like: ERROR: Failed to build gem native extension checking for ffi.h... no...

Resolve PhpStorm "Interpreter is not specified or invalid" on WAMP (Windows)

Symptom PhpStorm displays: "Interpreter is not specified or invalid. Press ‘Fix’ to edit your project configuration." This occurs when the IDE cannot locate a valid PHP CLI executable or when the debu...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.