PowerShell 实现监控主流三条技术路线: 轮询查询进程快照(最简通用方案) Get-Process WMI/CIM 进程事件订阅(事件驱动,优于轮询) Register-CimIndicationEvent P/Invoke Win32 API(检测窗口无响应、进程冻结) user32.dll::IsHungAppWindow
PowerShell 监视程序、自动重启崩溃 / 挂起应用|完整拆解、底层原理、链路与配套组件
一、整体方案定位
这类监控脚本核心目标两类场景:
- 进程意外退出(崩溃):进程正常 / 异常终止,不存在 PID;
- 进程挂起(卡死、无响应):进程仍然存在,但窗口无响应、主线程阻塞(GUI 程序为主)。
PowerShell 实现监控主流三条技术路线:
- 轮询查询进程快照(最简通用方案)
Get-Process - WMI/CIM 进程事件订阅(事件驱动,优于轮询)
Register-CimIndicationEvent - P/Invoke Win32 API(检测窗口无响应、进程冻结)
user32.dll::IsHungAppWindow
底层根基:Windows 进程管理器(ntoskrnl.exe Ps 子系统)+ Win32 窗口管理子系统。
关键区分:✅ 进程崩溃:内核 Ps 子系统销毁 EPROCESS 对象;✅ 程序挂起:进程存活,主线程消息循环阻塞,内核不会主动杀死进程。
二、完整调用栈树形结构
路线 1:轮询模式(Get-Process 最常用脚本方案)
plaintext
PowerShell(pwsh/powershell.exe)
↓ Get-Process Cmdlet
System.Diagnostics.Process (.NET托管封装)
↓ kernel32.dll
├─ OpenProcess
├─ EnumProcesses 枚举系统所有PID
└─ QueryProcessInformation 获取进程基础信息
↓ 两种分支判断
├─分支A:目标进程不在列表 → 判定崩溃,启动新实例 Start-Process
│
└─分支B:进程存在 → 额外判断是否挂起
↓ Add-Type P/Invoke user32.dll::IsHungAppWindow
↓ Dwm.exe / win32k.sys 窗口消息队列检测
↓ 判定挂起 → 可选择Kill进程后重启
路线 2:CIM WMI 事件订阅(事件驱动,推荐生产)
plaintext
PowerShell
↓ Register-CimIndicationEvent
winmgmt.exe (WMI服务)
↓ root/cimv2 Win32_ProcessStopTrace 进程停止事件
↓ 内核Ps子系统 进程退出通知
当进程崩溃/正常关闭时,WMI主动推送事件
脚本触发重启逻辑,无需高频轮询
优势:无循环空耗 CPU;短板:只能感知进程退出,无法识别 “进程活着但卡死”。
三、核心功能模块底层原理拆解
模块 1:检测进程是否存在(识别崩溃)
API 链路
EnumProcesses() → 获取全部 PID 列表 → OpenProcess() 查询进程镜像名
Get-Process 内部封装以上原生 API。
- 按进程名称(notepad.exe):简易,但存在同名多实例冲突;
- 按原始启动 PID:进程重启后 PID 改变,无法跟踪;
- 更可靠:记录启动时间 + 主窗口标题 + 镜像路径区分实例。
模块 2:检测 GUI 程序是否挂起(IsHungAppWindow)
c
运行
[DllImport("user32.dll")]
public static extern bool IsHungAppWindow(IntPtr hWnd);
底层原理:
user32.dll 向目标窗口投递消息,检测消息队列是否长期得不到处理;
阈值由系统内部会话管理器控制。
⚠️ 重要限制
- 仅适用于拥有顶层窗口的 GUI 程序;
- 控制台程序、后台服务、无窗口进程无法使用该 API 判断是否卡死;
- 后台服务阻塞、业务逻辑卡死,没有通用原生 API 识别,只能依靠「业务心跳端口 / 日志 / 自定义健康接口」。
模块 3:终止异常进程(挂起后清理)
powershell
$proc | Stop-Process -Force
链路:
TerminateProcess() kernel32.dll → 内核 PsTerminateProcess,强制销毁进程地址空间。模块 4:启动应用(Start-Process)
封装
CreateProcessW(),创建新进程会话,继承安全令牌。四、依赖文件、系统配套清单
用户态核心 DLL
- kernel32.dll:进程枚举、OpenProcess、TerminateProcess、CreateProcess
- user32.dll:IsHungAppWindow(窗口无响应检测)
- advapi32.dll:可选,查询进程安全信息、权限
- System.Diagnostics.Process (.NET 托管层,Get-Process 底层载体)
系统服务
- Winmgmt(WMI):使用 CIM 事件订阅方案必需;轮询方案不需要。
- RPC:WMI 通信依赖。
内核组件
- ntoskrnl.exe/ Ps 进程管理器:维护系统进程列表、EPROCESS 结构
- win32k.sys:窗口子系统、消息队列(GUI 挂起检测)
权限约束
- 监控普通应用:普通用户权限基本够用;
- 终止系统进程、服务进程、受保护进程:必须管理员权限;
- 部分受保护进程(杀毒、系统关键进程)OpenProcess 会被拒绝访问。
五、两种架构优劣对比
方案 A:While 循环 + Get-Process 轮询(通用脚本)
powershell
while($true){
$proc = Get-Process "MyApp" -ErrorAction SilentlyContinue
if(-not $proc){
Start-Process "C:\MyApp.exe"
}
Start-Sleep 3
}
✅ 优点:兼容所有 Windows 版本;不需要 WMI;同时支持崩溃 + GUI 挂起检测
❌ 缺点:周期性轮询,存在最大「睡眠间隔」延时;高频轮询轻微消耗 CPU
方案 B:CIM 进程停止事件订阅(事件驱动)
powershell
Register-CimIndicationEvent -ClassName Win32_ProcessStopTrace ...
✅ 优点:事件触发,无轮询、实时性高、资源开销极低
❌ 致命短板:只能捕获进程退出,不能识别 “进程活着但卡死”;控制台无窗口程序无法判定挂起。
六、底层典型坑点(运维高频问题)
-
同名多实例程序误重启只按进程名称判断,一个实例崩溃会把所有实例全部重启。解决:记录完整程序路径区分实例,不要只靠 Name。
-
IsHungAppWindow 对控制台程序无效控制台程序没有顶层 HWND 窗口句柄,无法检测卡死;解决方案:应用内置 TCP 心跳、日志文件轮询、自定义健康检测接口。
-
进程 “僵尸 / 孤立”、无法 Kill进程处于内核阻塞状态(驱动等待 IO),TerminateProcess 失败;只能重启电脑。
-
脚本自身崩溃 → 监控失效PowerShell 脚本本身没有监护;生产环境建议:
- 将监控脚本注册为Windows 服务(nssm);
- 双层监护:外部看门狗监控 powershell 进程。
-
快速反复重启造成风暴程序本身存在 Bug,启动立刻崩溃,脚本无限循环创建进程。工程化方案:增加冷却时间、失败计数器,连续失败 N 次暂停重启。
-
权限问题:脚本以普通用户运行,无法启动需要管理员的程序Start-Process 添加
-Verb RunAs提升;或监控脚本本身以管理员身份常驻。
七、完整数据流示例(轮询监控 GUI 程序)
- PowerShell 进入 while 循环,Sleep 间隔 3 秒;
- 调用 Get-Process 枚举进程,查找目标程序;
- 情形 1:找不到进程 → 判断崩溃,调用 Start-Process 启动程序;
- 情形 2:进程存在,获取 MainWindowHandle;
- P/Invoke 调用 IsHungAppWindow 检测窗口是否无响应;
- 如果挂起:Stop-Process -Force 强制杀死;等待短暂延时;启动新进程;
- 写入日志,循环休眠等待下一轮检测。
八、工程扩展分层设计(可直接落地)
- 采集层:轮询 / CIM 事件
- 健康判断层
- 基础:进程是否存在
- 增强:GUI 窗口是否挂起
- 业务层:TCP 端口连通性、HTTP 健康接口、日志更新检测
- 保护策略层:重启冷却、失败次数阈值、告警输出(事件日志 / 文件)
- 执行层:Kill 异常进程 → 延时 → 启动应用
- 日志层:记录崩溃时间、重启原因、前后进程 PID
PowerShell 脚本来监视并自动重新启动崩溃或挂起的应用程序。以下是一个简单的示例脚本,用于监视并重新启动特定的应用程序:
powershellCopy Code
# 设置要监视的应用程序名称
$applicationName = "YourApplication.exe"
# 设置检测时间间隔(秒)
$checkInterval = 5
# 循环监视应用程序状态
while ($true) {
$process = Get-Process | Where-Object { $_.ProcessName -eq $applicationName }
if (-not $process) {
Write-Host "$applicationName 崩溃,正在重新启动..."
Start-Process -FilePath "C:\Path\To\Your\Application.exe"
}
Start-Sleep -Seconds $checkInterval
}
在这个示例脚本中,您需要将 "YourApplication.exe" 替换为您要监视的应用程序的实际名称,并将 C:\Path\To\Your\Application.exe 替换为应用程序的实陵路径。
CMD 批处理脚本来监视并自动重新启动崩溃或挂起的应用程序。以下是一个简单的示例脚本,用于监视并重新启动特定的应用程序:
Copy Code
@echo off
:: 设置要监视的应用程序名称
set applicationName=YourApplication.exe
:: 设置检测时间间隔(秒)
set checkInterval=5
:: 循环监视应用程序状态
:loop
tasklist | findstr /i %applicationName% >nul
if errorlevel 1 (
echo %applicationName% 崩溃,正在重新启动...
start "" "C:\Path\To\Your\Application.exe"
)
timeout /t %checkInterval% /nobreak >nul
goto :loop
在这个示例脚本中,您需要将 YourApplication.exe 替换为您要监视的应用程序的实际名称,并将 C:\Path\To\Your\Application.exe 替换为应用程序的实际路径。
PowerShell 实现自动重新启动崩溃或挂起的服务,您可以按照以下步骤操作:
-
获取服务名称:
- 确定您要监视和重新启动的服务的名称。可以使用
Get-Servicecmdlet 来获取系统中所有服务的列表。
- 确定您要监视和重新启动的服务的名称。可以使用
-
编写 PowerShell 脚本:
- 编写一个 PowerShell 脚本来监视并重新启动服务。下面是一个简单的示例脚本:
powershellCopy Code
# 设置要监视和重新启动的服务名称
$serviceName = "YourServiceName"
# 设置检测时间间隔(秒)
$checkInterval = 5
# 循环监视服务状态
while ($true) {
$service = Get-Service | Where-Object { $_.Name -eq $serviceName }
if (-not $service.Status -eq "Running") {
Write-Host "$serviceName 服务崩溃或挂起,正在重新启动..."
Restart-Service -Name $serviceName
}
Start-Sleep -Seconds $checkInterval
}
在这个示例脚本中,您需要将 "YourServiceName" 替换为您要监视和重新启动的服务的实际名称。
- 运行脚本:
- 将脚本保存为 .ps1 文件,并在管理员权限下运行 PowerShell 控制台。
- 使用
Set-ExecutionPolicy RemoteSigned命令来设置脚本执行策略,以允许运行本地脚本。 - 运行您编写的 PowerShell 脚本来监视并重新启动服务。

浙公网安备 33010602011771号