Windows 重启 explorer 的正确做法
目录
一、关于 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 + 1BOOL 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.
相关文章:

Windows 重启 explorer 的正确做法
目录 一、关于 Restart Manager 二、重启管理器实例 三、完整实现代码和测试 本文属于原创文章,转载请注明出处: https://blog.csdn.net/qq_59075481/article/details/136179191。 我们往往使用 TerminateProcess 并传入 PID 和特殊结束代码 1 或者…...

linux基础学习(10):基本权限与相关命令
1.基本权限 用ls -l查看当前目录文件时,可以看到文件的基本权限 其由10位组成,其中: 第1位:代表文件类型。 - d lbc普通文件目录文件软链接文件块设备文件,也就是硬盘等存储设备的文件字符设备文件,是鼠…...
木马植入方式及防范手段
木马植入方式: 1. 诱骗下载和安装:通过欺骗、社交工程等手段,诱使用户下载和安装包含木马的软件或文件。 2. 隐秘附加:将木马隐藏在合法软件的背后,并伴随软件一起安装,用户在不知情的情况下也会安装木马。…...

Unity3D中刚体、碰撞组件、物理组件的区别详解
前言 Unity3D提供了丰富的功能和组件,其中包括刚体、碰撞组件和物理组件。这些组件在游戏开发中起着非常重要的作用,能够让游戏世界更加真实和有趣。本文将详细介绍这三种组件的区别以及如何在Unity3D中实现它们。 对惹,这里有一个游戏开发…...

Java实现Redis延时队列
“如何实现Redis延时队列”这个面试题应该也是比较常见的,解答如下: 使用sortedset(有序集合) ,拿时间戳作为 score ,消息内容作为key 调用 zadd 来生产消息,消费者用zrangebyscore 指令获取 N …...
Selenium折线图自动化测试
目录 获取折线图echarts实例 获取折线图实例锚点的坐标 通过echarts实例的getOption()方法获取坐标数据 将折线图坐标点转换为像素坐标值 整合折线图坐标数据 根据折线图坐标计算出锚点相对于浏览器中的坐标 计算canvas画布原点的坐标 计算折线图相对于浏览器的坐标 使用…...

<网络安全>《41 网络攻防专业课<第七课 - IIS上传和Tomcat弱口令漏洞攻击与防范>》
1 中间件PUT漏洞介绍 中间件包括apache、tomcat、IIS、weblogic等,这些中间件可以设置支持的HTTP方法。(HTTP方法包括GET、POST、HEAD、DELETE、PUT、OPTIONS等) 每一个HTTP方法都有其对应的功能,在这些方法中,PUT可…...

云计算基础-虚拟化概述
虚拟化概述 虚拟化是一种资源管理技术,能够将计算机的各种实体资源(如CPU、内存、磁盘空间、网络适配器等)予以抽象、转换后呈现出来并可供分割、组合为一个或多个逻辑上的资源。这种技术通过在计算机硬件上创建一个抽象层,将单台…...
ElementUI +++ Echarts面试题答案汇总
官网地址:http://element-cn.eleme.io/#/zh-CN ElementUI是一套基于VUE2.0的桌面端组件库,ElementUI提供了丰富的组件帮助开发人员快速构建功能强大、风格统一的页面。 ElementUi是怎么做表单验证的?在循环里对每个input验证怎么做呢&#x…...

notepad++打开文本文件乱码的解决办法
目录 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码,红框圈出来的一个个试。我切换到UTF-8编码就正常了。 乱码如图。下面分享我的解决办法 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码&#…...
道可云元宇宙每日资讯|上海开放大学发布“智慧学习中心元宇宙”
道可云元宇宙每日简报(2024年2月19日)讯,今日元宇宙新鲜事有: 上海开放大学发布“智慧学习中心元宇宙” 上海开放大学首次发布了“智慧学习中心元宇宙”,初步构筑了上海开放大学5g元宇宙孪生学习环境,13所…...

压缩感知(Compressed Sensing,CS)的基础知识
压缩感知(Compressed Sensing,CS)是一种用于信号处理的技术,旨在以少于奈奎斯特采样定理所要求的样本频率来重构信号。该技术利用信号的稀疏性,即信号可以用较少的非零系数表示。压缩感知在图像获取中的应用使得在采集…...

如何系统地学习Python
建议系统学习Python的途径遵循理论与实践相结合的教学方法。以下是一个分阶段的学习计划: 阶段一:基础知识 理解Python的特点: 认识Python的历史与设计哲学。学习Python的基本语法和运行环境。 安装Python: 学习如何在不同操作系…...
SMT2020:半导体制造流程标准仿真测试数据介绍
文章目录 问题背景SMT2020 涉及的主要功能1. 包含多种仿真模型类型2. 包含非计划性动作3. 区分不同类型设备的加工速率4. 特殊的复杂操作SMT2020 数据概览1. AutoSched 仿真模型数据2. General Data 输入数据问题背景 在半导体的生产制造当中,由于晶元片及设备等的高价值性,…...

沁恒CH32V30X学习笔记11---使用外部时钟模式2采集脉冲计数
使用外部时钟模式2采集脉冲计数 使用外部触发模式 2 能在外部时钟引脚输入的每一个上升沿或下降沿计数。将 ECE 位置位时,将使用外部时钟源模式 2。使用外部时钟源模式 2 时,ETRF 被选定为 CK_PSC。ETR 引脚经过可选的反相器(ETP),分频器(ETPS)后成为 ETRP,再经过滤波…...

ffmpeg for android编译全过程与遇到的问题
编译前准备 编译环境:Ubuntu16,可自行下载VMWare最新版并百度永久许可证或在服务器上安装Ubuntu ffmpeg源码:ffmpeg4.2.2 NDK下载:Android NDK r21e 有条件的最好还是在Liunx平台下编译吧,Windows平台下编译坑更多…...

【无标题】力扣报错:member access within null pointer of type ‘struct ListNode‘
项目场景: 做单链表反转题目,报错:member access within null pointer of type ‘struct ListNode’ 题目链接:LINK 问题描述 我明明在初始化指针时候,已经处理了n2->next情况却依然报错 这个报错提示含义是:大概就…...
Qt之Qchar类的接口1
Qt类的构造函数 QChar类提供了许多个不同原型的构造函数,以方便不同场合下使用。 QChar(); //构造一个空字符,即‘\0’ QChar(char ch); //由字符数据ch构造 QChar(uchar ch)…...

vue的十大面试题详情
1 v-show与v-if区别 v-if与v-show可以根据条件的结果,来决定是否显示指定内容: v-if: 条件不满足时, 元素不会存在. v-show: 条件不满足时, 元素不会显示(但仍然存在). <div id"app"><button click"show !show">点我</but…...

(十四)devops持续集成开发——jenkins流水线使用pipeline方式发布项目
前言 本节内容我们使用另外一种方式pipeline实现项目的流水线部署发布,Jenkins Pipeline是一种允许以代码方式定义持续集成和持续交付流水线的工具。通过Jenkins Pipeline,可以将整个项目的构建、测试和部署过程以脚本的形式写入Jenkinsfile中ÿ…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...