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 
#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 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 结构来标识资源。参数解释如下:

Windows 重启 explorer 的正确做法_第1张图片 参数解释

代码如下: 

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 
#include 
#include 
#include 
#include 

#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(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 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);
    }
}

测试效果如图所示:

Windows 重启 explorer 的正确做法_第2张图片 测试结果截图

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


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

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

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

你可能感兴趣的:(Windows,基础编程,windows,微软,学习方法,c++)