重启资源管理器进程的方法不唯一,但长期以来大家对实施方法用的不到位。
在上一篇中我认为:“我们往往使用 TerminateProcess 并传入 PID 和特殊结束代码 1 或者 taskkill /f /im 等方法重启资源管理器( explorer.exe ),其实这是不正确的。我们应该使用重启管理器接口来重启 explorer 进程。”
其实,并不能一口咬定以前的方法不正确。我们应该想想为什么 TerminateProcess 似乎必须传入结束代码 1 ,才能不让资源管理器自动重新启动?
要知其然,更要知其所以然。
系列文章:
- 1.Windows 重启 explorer 的正确做法-CSDN博客
- 2.重启 explorer 进程的正确做法(二)
在注册表中,有一个叫 “AutoRetartShell” 的注册表值项,该值是 BOOL 类型。当值为 1 时,资源管理器会在结束时通知 Winlogon 并由 Winlogon 自动重新启动 explorer。反之,当该值为 0 时,则不会自动重新启动。
在修改注册表之前,资源管理器在退出时会被自动重启,只有指定特殊的退出状态,才能阻止自动重启。而在修改好注册表后,就不会触发自动重启了。所以,我们正确的做法不是去研究应该用什么进程退出状态码,而是通过临时修改注册表中的关键设置,来屏蔽自动重启功能。
所以,我们正确的做法是,在调用 TerminateProcess 之前,修改注册表 “AutoRetartShell” 的值为 0 ,然后再在结束进程后恢复值为 1 即可。(通常需要管理员权限)
使用下面的代码修改注册表:
/**
* @brief 结束 explorer 进程之前,需要首先检查注册表配置
*
* @param[in]
*
* @return TRUE 操作成功完成;
* FALSE 操作失败
* @note
*/
BOOL SetRegistryValue(LPCWSTR lpSubKey, LPCWSTR lpValueName, DWORD dwData)
{
HKEY hKey = NULL;
DWORD dwDisposition = 0;
//DWORD dwData = bEnable ? 3 : 0; // 默认值为 3,bEnable 作为开关变量
// 尝试打开或创建注册表键
LONG lResult = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
lpSubKey, // L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
NULL,
&hKey,
&dwDisposition);
if (lResult == ERROR_SUCCESS) {
DWORD dwType = 0;
DWORD dwValue = 0;
DWORD dwSize = sizeof(DWORD);
// 检查值是否存在
lResult = RegQueryValueExW(hKey, lpValueName,
NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
if (lResult == ERROR_SUCCESS) {
if (dwType == REG_DWORD && dwValue == dwData) {
// 值已存在且为 dwData,无需更新
RegCloseKey(hKey);
return TRUE;
}
else {
// 值存在但不为 dwData,更新值为 dwData
lResult = RegSetValueExW(hKey, lpValueName, // L"SoftwareSASGeneration"
0, REG_DWORD, (BYTE*)&dwData, sizeof(DWORD));
RegCloseKey(hKey);
return (lResult == ERROR_SUCCESS);
}
}
else if (lResult == ERROR_FILE_NOT_FOUND) {
// 值不存在,创建并设置为 dwData
lResult = RegSetValueExW(hKey, lpValueName,
0, REG_DWORD, (BYTE*)&dwData, sizeof(DWORD));
RegCloseKey(hKey);
return (lResult == ERROR_SUCCESS);
}
else {
// 其他错误
RegCloseKey(hKey);
return FALSE;
}
}
else {
// 打开或创建键失败
return FALSE;
}
}
调用方法如下所示:
if (SetRegistryValue(L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
L"AutoRestartShell", 0)) {
DWORD dwStatus = 0;
if (!TerminateProcess(......))
{
bResponse = dwStatus; // 如果调用失败,返回非零错误码
}
::SetRegistryValue(L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
L"AutoRestartShell", 1);
}else { // 设置注册表失败
bResponse = 50037;
LogEvent(50037, TEXT("Failed to update LogonType value.\n"));
}
使用 TerminateProcess 结束当前会话的 explorer 进程的代码如下:
#include <Windows.h>
#include <iostream>
#include <WtsApi32.h>
#pragma comment(lib, "Wtsapi32.lib")
int main()
{
PWTS_PROCESS_INFOW process_info;
DWORD process_num = 0;
DWORD current_session_id = 0;
ProcessIdToSessionId(GetCurrentProcessId(), ¤t_session_id);
if (!WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE,
0, 1, &process_info, &process_num))
{
WTSFreeMemory(process_info);
return false;
}
DWORD pid = 0;
for (UINT i = 0; i < process_num; i++)
{
if (current_session_id == process_info[i].SessionId &&
wcscmp(process_info[i].pProcessName, L"explorer.exe") == 0)
{
pid = process_info[i].ProcessId;
break;
}
}
WTSFreeMemory(process_info);
if (pid != 0)
{
HANDLE hProcessHandle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (TerminateProcess(hProcessHandle, 1)) // 修改注册表后,则不需要该值为 1
{
CloseHandle(hProcessHandle);
return 0;
}
}
return 1;
}
发布于:2024.03.10.