Windows 重启 explorer 的正确做法

news2024/11/23 22:49:59

目录

一、关于 Restart Manager

二、重启管理器实例

三、完整实现代码和测试


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136179191。

我们往往使用 TerminateProcess 并传入 PID 和特殊结束代码 1 或者 taskkill /f /im 等方法重启资源管理器( explorer.exe ),其实这是不正确的。我们应该使用重启管理器接口来重启 explorer 进程。

一、关于 Restart Manager

重启管理器 API 可以消除或减少完成安装或更新所需的系统重启次数。软件更新在安装或更新期间需要重启系统的主要原因是,正在运行的应用程序或服务当前正在使用某些正在更新的文件。 重启管理器允许关闭并重启除关键系统服务外的所有服务 。这会释放正在使用的文件,并允许完成安装操作。

安装程序可以使用重启管理器来注册应在安装应用程序或更新期间替换的文件。然后,在后续更新或安装期间,安装程序可以使用重启管理器来确定哪些文件因当前正在使用而无法更新。重启管理器可以关闭并重启当前使用这些文件的非关键服务或应用程序。安装程序可以指示重启管理器根据正在使用的文件、进程 ID (PID) 或 Windows 服务的短名称来关闭和重启应用程序或服务。

二、重启管理器实例

实现一个简易的重启管理器实例主要需要 RmStartSession 、RmRegisterResources、RmShutdown、RmEndSession 等函数。

首先我们需要在代码中引入头文件:

#include <restartmanager.h>
#pragma comment(lib, "Rstrtmgr.lib")

然后我们用 RmStartSession 创建会话。其中会话的密钥是 GUID ,为其分配缓冲区时候需要计算好缓冲区大小。

#define RM_SESSIONKEY_LEN sizeof(GUID) * 2 + 1
DWORD dwRmStatus = 0;
DWORD dwSessionHandle = 0;
WCHAR strSessionKey[RM_SESSIONKEY_LEN] = { 0 };
dwRmStatus = RmStartSession(&dwSessionHandle, NULL, strSessionKey);
if (ERROR_SUCCESS != dwRmStatus)
{
    std::cerr << "RmStartSession failed: " << std::dec << dwRmStatus << std::endl;
    return -1;
}

随后需要将要重启的进程信息放到 RM_UNIQUE_PROCESS 结构体数组中,该结构体如下:

typedef struct _RM_UNIQUE_PROCESS {
  DWORD    dwProcessId;
  FILETIME ProcessStartTime;
} RM_UNIQUE_PROCESS, *PRM_UNIQUE_PROCESS;

第一个是进程的 PID,第二个是进程的创建时间,使用 GetProcessTimes 函数的 lpCreationTime 参数获取。

BOOL GetProcessTimes(
  [in]  HANDLE     hProcess,
  [out] LPFILETIME lpCreationTime,
  [out] LPFILETIME lpExitTime,
  [out] LPFILETIME lpKernelTime,
  [out] LPFILETIME lpUserTime
);

我使用 Toolhelp32 枚举 explorer 进程,并生成 RM_UNIQUE_PROCESS 结构体数组。

BOOL GetShellProcessRmInfoEx(
    PRM_UNIQUE_PROCESS* lpRmProcList, 
    DWORD_PTR* lpdwCountNum
)
{
    PROCESSENTRY32W pe32 = { 0 };
    FILETIME lpCreationTime = { 0 };
    FILETIME lpExitTime = { 0 };
    FILETIME lpKernelTime = { 0 };
    FILETIME lpUserTime = { 0 };
    HANDLE hProcess = nullptr;
    RM_UNIQUE_PROCESS tpProc = { 0 };
    std::vector<RM_UNIQUE_PROCESS> RmProcVec;
    SIZE_T VecLength = 0;

    // 在使用这个结构前,先设置它的大小
    pe32.dwSize = sizeof(pe32);

    // 给系统内所有的进程拍个快照
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        std::cerr << "CreateToolhelp32Snapshot 调用失败." << std::endl;
        return FALSE;
    }

    // 遍历进程快照,轮流显示每个进程的信息
    BOOL bMore = Process32FirstW(hProcessSnap, &pe32);
    while (bMore)
    {
        if (!_wcsicmp(pe32.szExeFile, L"explorer.exe"))
        {
            hProcess = OpenProcess(
                PROCESS_QUERY_LIMITED_INFORMATION, 
                FALSE, pe32.th32ProcessID);
            if (hProcess != nullptr)
            {
                memset(&lpCreationTime, 0, sizeof(FILETIME));

                if (GetProcessTimes(hProcess,
                    &lpCreationTime, &lpExitTime,
                    &lpKernelTime, &lpUserTime) == TRUE)
                {
                    tpProc.dwProcessId = pe32.th32ProcessID;
                    tpProc.ProcessStartTime = lpCreationTime;
                    RmProcVec.push_back(tpProc);
                }

                CloseHandle(hProcess);
                hProcess = nullptr;
            }
        }
        bMore = Process32NextW(hProcessSnap, &pe32);
    }

    // 清除 snapshot 对象
    CloseHandle(hProcessSnap);

    VecLength = RmProcVec.size();

    if (VecLength != 0 && VecLength < (SIZE_T)0xf4236u)
    {
        RM_UNIQUE_PROCESS* lprmUniqueProc = 
            new(std::nothrow) RM_UNIQUE_PROCESS[VecLength];

        if (lprmUniqueProc != nullptr)
        {
            SIZE_T rSize = VecLength * sizeof(RM_UNIQUE_PROCESS);

            if (rSize < (SIZE_T)0xC80000u && rSize > 0)
            {
                if (!memcpy_s(lprmUniqueProc, rSize, &RmProcVec[0], rSize))
                {
                    *lpdwCountNum = VecLength;
                    *lpRmProcList = lprmUniqueProc;
                    return TRUE;
                }
            }
            else {
                std::cerr << "Vector Size to large!" << std::endl;
            }
        }
        else {
            std::cerr << "Alloc memory failed!" << std::endl;
        }
    }
    else {
        std::cerr << "Vector Size to large!" << std::endl;
    }
    return FALSE;
}

使用 RmRegisterResources 函数将资源注册到重启管理器会话。重启管理器使用向会话注册的资源列表来确定必须关闭和重启哪些应用程序和服务。 可以通过文件名、服务短名称或描述正在运行的应用程序 RM_UNIQUE_PROCESS 结构来标识资源。参数解释如下:

参数解释

代码如下: 

dwRmStatus = RmRegisterResources(dwSessionHandle,
        0, NULL, dwNum, lpRmProcList, 0, NULL);
if (ERROR_SUCCESS != dwRmStatus)
{
    std::cerr << "RmRegisterResources failed: " << std::dec << dwRmStatus << std::endl;
    RmProcMemFree(lpRmProcList, lpdwCountNum);
    return -1;
}

然后使用 RmShutdown 就可以请求结束进程,可以指定第二个参数来选择强制或者非强制两种状态。

完成任务后使用 RmRestart 重新启动被关闭的进程即可,程序会尝试恢复并刷新状态。

所有操作完成后,使用 RmEndSession 关闭会话句柄。

三、完整实现代码和测试

完整代码如下:

#include <iostream>
#include <Windows.h>
#include <restartmanager.h>
#include <TlHelp32.h>
#include <vector>

#pragma comment(lib, "Rstrtmgr.lib")

#define RM_SESSIONKEY_LEN sizeof(GUID) * 2 + 1

BOOL GetShellProcessRmInfoEx(
    PRM_UNIQUE_PROCESS* lpRmProcList,
    DWORD_PTR* lpdwCountNum
);
void RmProcMemFree(
    PRM_UNIQUE_PROCESS lpRmProcList,
    DWORD_PTR lpdwCountNum
);

void RmWriteStatusCallback(
    UINT nPercentComplete
);

int main()
{
    DWORD dwRmStatus = 0;
    DWORD dwSessionHandle = 0;
    WCHAR strSessionKey[RM_SESSIONKEY_LEN] = { 0 };
    dwRmStatus = RmStartSession(&dwSessionHandle, NULL, strSessionKey);
    if (ERROR_SUCCESS != dwRmStatus)
    {
        std::cerr << "RmStartSession failed: " << std::dec << dwRmStatus << std::endl;
        return -1;
    }
    
    PRM_UNIQUE_PROCESS lpRmProcList = nullptr;
    DWORD_PTR lpdwCountNum = 0;
    
    if (!GetShellProcessRmInfoEx(&lpRmProcList, &lpdwCountNum))
    {
        std::cerr << "GetShellProcessRmInfoEx failed."<< std::endl;
        return -1;
    }

    UINT dwNum = static_cast<UINT>(lpdwCountNum);
    std::cout << "Process Count: " << dwNum << std::endl;
    std::cout << "Shell PID: "<< std::endl;
    for (UINT i = 0; i < dwNum; i++)
    {
        std::cout << " > " << lpRmProcList[i].dwProcessId << std::endl;

    }

    dwRmStatus = RmRegisterResources(dwSessionHandle,
        0, NULL, dwNum, lpRmProcList, 0, NULL);
    if (ERROR_SUCCESS != dwRmStatus)
    {
        std::cerr << "RmRegisterResources failed: " << std::dec << dwRmStatus << std::endl;
        RmProcMemFree(lpRmProcList, lpdwCountNum);
        return -1;
    }

    dwRmStatus = RmShutdown(dwSessionHandle, RmForceShutdown, RmWriteStatusCallback);
    if (ERROR_SUCCESS != dwRmStatus)
    {
        std::cerr << "RmShutdown failed: " << std::dec << dwRmStatus << std::endl;
        // return -1;
    }

    Sleep(5000);

    dwRmStatus = RmRestart(dwSessionHandle, 0, RmWriteStatusCallback);
    if (ERROR_SUCCESS != dwRmStatus)
    {
        std::cerr << "RmRestart failed: " << std::dec << dwRmStatus << std::endl;
        // return -1;
    }

    dwRmStatus = RmEndSession(dwSessionHandle);
    if (ERROR_SUCCESS != dwRmStatus)
    {
        std::cerr << "RmEndSession failed: " << std::dec << dwRmStatus << std::endl;
        // return -1;
    }

    RmProcMemFree(lpRmProcList, lpdwCountNum);
    return 0;
}


void RmWriteStatusCallback(
    UINT nPercentComplete
)
{
    std::cout << "Task completion level: " << std::dec << nPercentComplete << std::endl;
}

BOOL GetShellProcessRmInfoEx(
    PRM_UNIQUE_PROCESS* lpRmProcList, 
    DWORD_PTR* lpdwCountNum
)
{
    PROCESSENTRY32W pe32 = { 0 };
    FILETIME lpCreationTime = { 0 };
    FILETIME lpExitTime = { 0 };
    FILETIME lpKernelTime = { 0 };
    FILETIME lpUserTime = { 0 };
    HANDLE hProcess = nullptr;
    RM_UNIQUE_PROCESS tpProc = { 0 };
    std::vector<RM_UNIQUE_PROCESS> RmProcVec;
    SIZE_T VecLength = 0;

    // 在使用这个结构前,先设置它的大小
    pe32.dwSize = sizeof(pe32);

    // 给系统内所有的进程拍个快照
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        std::cerr << "CreateToolhelp32Snapshot 调用失败." << std::endl;
        return FALSE;
    }

    // 遍历进程快照,轮流显示每个进程的信息
    BOOL bMore = Process32FirstW(hProcessSnap, &pe32);
    while (bMore)
    {
        if (!_wcsicmp(pe32.szExeFile, L"explorer.exe"))
        {
            hProcess = OpenProcess(
                PROCESS_QUERY_LIMITED_INFORMATION, 
                FALSE, pe32.th32ProcessID);
            if (hProcess != nullptr)
            {
                memset(&lpCreationTime, 0, sizeof(FILETIME));

                if (GetProcessTimes(hProcess,
                    &lpCreationTime, &lpExitTime,
                    &lpKernelTime, &lpUserTime) == TRUE)
                {
                    tpProc.dwProcessId = pe32.th32ProcessID;
                    tpProc.ProcessStartTime = lpCreationTime;
                    RmProcVec.push_back(tpProc);
                }

                CloseHandle(hProcess);
                hProcess = nullptr;
            }
        }
        bMore = Process32NextW(hProcessSnap, &pe32);
    }

    // 清除 snapshot 对象
    CloseHandle(hProcessSnap);

    VecLength = RmProcVec.size();

    if (VecLength != 0 && VecLength < (SIZE_T)0xf4236u)
    {
        RM_UNIQUE_PROCESS* lprmUniqueProc = 
            new(std::nothrow) RM_UNIQUE_PROCESS[VecLength];

        if (lprmUniqueProc != nullptr)
        {
            SIZE_T rSize = VecLength * sizeof(RM_UNIQUE_PROCESS);

            if (rSize < (SIZE_T)0xC80000u && rSize > 0)
            {
                if (!memcpy_s(lprmUniqueProc, rSize, &RmProcVec[0], rSize))
                {
                    *lpdwCountNum = VecLength;
                    *lpRmProcList = lprmUniqueProc;
                    return TRUE;
                }
            }
            else {
                std::cerr << "Vector Size to large!" << std::endl;
            }
        }
        else {
            std::cerr << "Alloc memory failed!" << std::endl;
        }
    }
    else {
        std::cerr << "Vector Size to large!" << std::endl;
    }
    return FALSE;
}

void RmProcMemFree(
    PRM_UNIQUE_PROCESS lpRmProcList, 
    DWORD_PTR lpdwCountNum)
{
    __try
    {
        DWORD_PTR dwCountNum = lpdwCountNum;

        if (lpRmProcList != nullptr && dwCountNum > 0)
        {
            while (--dwCountNum)
            {
                if (IsBadWritePtr(&lpRmProcList[dwCountNum], sizeof(RM_UNIQUE_PROCESS)))
                {
                    throw(L"BadWritePtr event!");
                    break;
                }

                memset(&lpRmProcList[dwCountNum], 0, sizeof(RM_UNIQUE_PROCESS));
            }

            delete[] lpRmProcList;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        // 处理 SEH 异常
        std::cerr << "Error access violation." << std::endl;
        exit(-1);
    }
}

测试效果如图所示:

测试结果截图

测试内容:首先结束所有 explorer 进程,随后重启进程。 


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136179191。

文章发布于:2024.02.19,更新于:2024.02.19.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1457704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

rocketMQ-Dashboard安装与部署

1、下载最新版本rocketMQ-Dashboard 下载地址&#xff1a;https://github.com/apache/rocketmq-dashboard 2、下载后解压&#xff0c;并用idea打开 3、修改配置 ①、修改端口及rocketmq服务的ip&#xff1a;port ②、修改访问账号、密码 3、然后启动访问&#xff1a; 4、mav…

单片机01天_stm32f407zg_创建新工程

创建“寄存器版工程” 1、创建工程文件夹 创建工程文件夹“Project”&#xff0c;内部包含文件夹“CMSIS”&&“USER”。 CMSIS&#xff1a;Cortex系列内核接口驱动文件。 USER&#xff1a;存放用户编写的程序文件。 “USER”文件夹内包含“Inc”&&“Src”…

yolov5的Mosaic原理解析

众所周知&#xff0c;yolov5中使用了mosaic增强进行数据增强&#xff0c;效果就是将4张图片拼凑为1张图片。为了更好优化自定义任务&#xff0c;特对mosaic原理进行解析。 1、mosaic原理解析 mosaic增强的原理一张图就可以解释&#xff1a; 1.1 图的注释 首先高亮区域&am…

YOLOv8-Openvino和ONNXRuntime推理【CPU】

1 环境&#xff1a; CPU&#xff1a;i5-12500 2 安装Openvino和ONNXRuntime 2.1 Openvino简介 Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包&#xff0c;主要用于对深度推理做优化。 Openvino内部集成了Opencv、TensorFlow模块&#xff0c;除此…

ClickHouse 基础(一)

官网 以毫秒为单位查询数十亿行 ClickHouse是用于实时应用和分析的最快、资源效率最高的开源数据库。 安装ClickHouse 使用ClickHouse&#xff0c;你有三个选择: ClickHouse云:官方ClickHouse作为一项服务&#xff0c;-由ClickHouse的创建者构建&#xff0c;维护和支持快速安…

C语言中关于#include的一些小知识

写代码的过程中&#xff0c;因为手误&#xff0c;重复包含了头文件 可以看到没有报错 如果是你自己编写的头文件&#xff0c;那么如果没加唯一包含标识的话&#xff0c;那么编译器会编译报错的。如果是系统自带的头文件&#xff0c;由于其每个头文件都加了特殊标识&#xff0c…

【软考】系统集成项目管理工程师(十六)变更管理【1分】

一、 变更的概念 1、定义、原因、分类 2、变更流程 二、 变更的原则 1、变更管理原则、配置管理工具 2、变更管理流程 三、 变更的流程及角色职责 1、提出变更申请、变更影响分析 2、变更测试 1、有些变更很小&#xff0c;客户着急要&#xff0c;可以不用走变更程序直接修改…

Android widget基础指南

widget的概念最早是由一名叫Rose的苹果工程师提出&#xff0c;后来经过多方面机缘巧合的发展下&#xff0c;便有了今天Android平台上的小组件widget&#xff0c;一般APP开发可能应用场景较少&#xff0c;最常见的莫过于天气APP的widget。但对于从事IOT或车载方向的同学&#xf…

FL Studio2024年最新21.2破解中文版本下载地址

FL Studio 21的中文版本是一款非常受欢迎的音乐制作软件&#xff0c;它为用户提供了丰富的功能和工具&#xff0c;使他们能够轻松创作和编辑音乐。以下是一些关于FL Studio 21中文版本的主要特点和功能&#xff1a; FL Studio 21 Win-安装包下载如下: https://wm.makeding.co…

- 工程实践 - 《QPS百万级的有状态服务实践》03 - 消息队列

本文属于专栏《构建工业级QPS百万级服务》 继续上篇《QPS百万级的有状态服务实践》02 - 冷启动和热更新。我们的架构如图1。上一章在热更新部分&#xff0c;我们引入了消息队列。本章我们介绍下各个消息队列的优缺点&#xff0c;并选择其中一个说下核心概念和原理。 图1 目前市…

【Go语言】Go语言的数据类型

GO 语言的数据类型 Go 语言内置对以下这些基本数据类型的支持&#xff1a; 布尔类型&#xff1a;bool 整型&#xff1a;int8、byte、int16、int、uint、uintptr 等 浮点类型&#xff1a;float32、float64 复数类型&#xff1a;complex64、complex128 字符串&#xff1a;st…

嵌入式学习 Day21

一. 文件IO: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …

基于STM32F407的coreJSON使用教程

目录 概述 工程建立 代码集成 函数介绍 使用示例 概述 coreJSON是FreeRTOS中的一个组件库&#xff0c;支持key查找的解析器&#xff0c;他只是一个解析器&#xff0c;不能生成json数据。同时严格执行 ECMA-404 JSON 标准。该库用 C 语言编写&#xff0c;设计符合 ISO C90…

杨氏矩阵和杨辉三角

杨氏矩阵 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N); 分析 若要满足要求时间复杂度小于O(N)&#xff0c;就不能每一行一个个…

机器学习基础(一)理解机器学习的本质

导读&#xff1a;在本文中&#xff0c;将深入探索机器学习的根本原理&#xff0c;包括基本概念、分类及如何通过构建预测模型来应用这些理论。 目录 机器学习 机器学习概念 相关概念 机器学习根本&#xff1a;模型 数据的语言&#xff1a;特征与标签 训练与测试&#xf…

elementui 中 el-date-picker 控制选择当前年之前或者之后的年份

文章目录 需求分析 需求 对 el-date-picker控件做出判断控制 分析 给 el-date-picker 组件添加 picker-options 属性&#xff0c;并绑定对应数据 pickerOptions html <el-form-item label"雨量年份&#xff1a;" prop"date"><el-date-picker …

正整数A+B(PTA团体天题练习题)细节题刨析

哎呀&#xff0c;又是看似简单的AB模型&#xff0c;这题确实也是AB&#xff0c;不过这个题让我debug1个多小时才找出来问题所在&#xff0c;服了&#xff0c;真是所谓细节决定成败&#xff0c;这题也挺值得记录下来的&#xff0c;话不多嗦&#xff0c;看题 题的目标很简单&…

[ansible] playbook运用

一、复习playbook剧本 --- - name: first play for install nginx #设置play的名称gather_facts: false #设置不收集facts信息hosts: webservers:dbservers #指定执行此play的远程主机组remote_user: root #指定执行此play的用…

css3的var()函数

css3的var()函数 变量要以两个连字符--(横杆)(减号)为开头 变量可以在:root{}中定义, :root可以在css中创建全局样式变量。通过 :root本身写的样式&#xff0c;相当于 html&#xff0c;但优先级比后者高。 在CSS3中&#xff0c;var()函数是一个用于插入CSS自定义属性&#xff…

如何创建WordPress付款表单(简单方法)

您是否正在寻找一种简单的方法来创建付款功能WordPress表单&#xff1f; 小企业主通常需要创建一种简单的方法来在其网站上接受付款&#xff0c;而无需设置复杂的购物车。简单的付款表格使您可以轻松接受自定义付款金额、设置定期付款并收集自定义详细信息。 在本文中&#x…