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.

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【软考中级备考笔记】计算机体系结构

计算机体系结构 2月19日 – 天气&#xff1a;阴转小雪 1. 冯诺依曼计算机体系结构 冯诺依曼将计算机分为了五大部分&#xff0c;分别是&#xff1a; 控制器&#xff1a;主要负责协调指令到执行运算器&#xff1a;负责算数和逻辑运算存储器&#xff1a;负责存储在指令执行过程…

自定义Linux登录自动提示语

设置提示语的方式 在Linux系统中&#xff0c;可以通过修改几个特定的文件来实现在用户登录时自动弹出提示语。以下是几个常用的方法&#xff1a; 1. 修改/etc/issue文件&#xff1a; 这个文件用于显示本地登录前的提示信息 sudo vi /etc/issue在项目合作的时候&#xff0c;…

springboot-vue-供应商招投标采购在线询价管理系统

1. 技术栈 后端&#xff08;Spring Boot&#xff09;: 控制器&#xff08;Controller&#xff09;处理HTTP请求。 服务层&#xff08;Service&#xff09;处理业务逻辑。 数据访问对象&#xff08;DAO&#xff09;与MySQL数据库交互。 Spring Security用于权限控制。 前端&…

最短路径分析

路径分析参考地址 路径分析参考地址2 路径分析参考地址3 1 绘制线,添加snap+拓扑检查 2 融合线–矢量叠加-用线分割 3 打开pgadmin4 - 创建数据–query tool 4 postgis链接postgres,密码000000 add files导入数据后,geom改为the_geom, srd o改为 4326 options ,勾选 点击…

【软考高级信息系统项目管理师--第二十章:高级项目管理】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;软考高级–信息系统项目管理师 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 第二十章&#xff1a;高级项目管理 项目集管理项目组合管理组织级项目管理OPM&…

Docker原理及概念相关

Docker最核心的组件 image&#xff1a;镜像&#xff0c;构建容器&#xff0c;也可以通过Dockerfile文本描述镜像的内容。 (我们将应用程序运行所需的环境&#xff0c;打包为镜像文件) Container&#xff1a;容器 (你的应用程序&#xff0c;就跑在容器中 ) 镜像仓库(dockerhub)(…

软件测试系列:移动端安卓APP测试必备之ADB命令 (二)

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

HttpClient:HTTP GET请求的服务器响应输出

前言 在现代软件开发中&#xff0c;与网络通信相关的技术变得愈发重要。Java作为一种强大而灵活的编程语言&#xff0c;提供了丰富的工具和库&#xff0c;用于处理各种网络通信场景。本文将聚焦在Java中使用HttpClient库发送HTTP GET请求&#xff0c;并将服务器的响应数据进行…

键盘输入4个数,从小到大排序

题目 键盘输入4个整数&#xff0c;从小到大排序 思路 代码 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h>//键盘输入4个整数&#xff0c;从小到大排序 int main() {int n1, n2, n3, n4;scanf_s("%d %d %d %d", &n1, &n2, &n3, &n4);…

嵌入式Linux学习DAY21--目录io

对主函数传参&#xff1a; ./a.out 参数1&#xff08;指针数组&#xff09;&#xff0c;参数2....... 在代码中&#xff0c;要写成 int main(参数数量&#xff0c;const char *指针数组) lseek&#xff08;a,b,c&#xff09;: 功能&#xff1a;重新设定文件描述符的偏移量 参…

群晖如何给Audio Station添加歌词插件(内含QQ、酷我、酷狗、网易云等歌词插件)

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📝 获取插件📝 添加插件📝 设置歌词📝 打开歌词⚓️ 相关链接 ⚓️📖 介绍 📖 本人将和大家分享如何给群晖的Audio Station套件添加歌词插件,从而让服务器上的歌曲可以自动的获取歌词 🏡 环境 🏡 本文

Java 基于 SpringBoot+Vue 的高校招生系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…