AI工具:个人电脑性能分析“四大名捕”,解决电脑慢上网慢问题
AI模型:Deepseek
开发方法:AI+人工
测试环境:windows11
保存:记事本ansi编码,所有类型,名称.ps1
运行:右键使用powershell运行
报权限错误方法:管理员运行powershell→Set-ExecutionPolicy RemoteSigned -Scope CurrentUser→y
代码:
1.内存分析工具:
说明:
- makeMemRptV3.ps1 – 进程内存分析报告
该脚本分析本地或远程计算机的进程内存占用,按工作集、私有内存等排序,取 Top N 个父进程,并展示每个父进程的直接子进程详情。
扩展信息包括:进程路径、关联服务(显示名称)、是否自启动、是否可关闭及关闭/禁用方法。
支持导出 CSV/HTML 报告。
主要参数:-Top(默认20)、-SortBy(WorkingSet/PrivateMemory/VirtualMemory/PagedMemory)、-OutputPath(支持 .csv/.html)、-ComputerName(远程需权限)。
示例:.\makeMemRptV3.ps1 -Top 10 -SortBy PrivateMemory -OutputPath report.html
适合排查高内存占用进程及其子进程,辅助系统优化。
代码:
点击查看代码
<#
.SYNOPSIS
生成进程内存使用分析报告,并显示每个进程的子进程内存占用(Top N)
扩展功能:显示进程路径、关联服务(显示名称)、是否可关闭、关闭/禁用方法
.DESCRIPTION
分析进程内存占用(工作集/私有内存等),按指定内存字段排序,取前 N 个进程。
对每个进程,列出其直接子进程的名称、PID、内存、路径、关联服务(显示名称)、
是否可关闭以及建议的关闭/禁用方法。
支持导出 CSV/HTML,自动识别本地计算机避免 WinRM 依赖。
.PARAMETER Top
显示内存占用最高的 N 个父进程,默认 20。
.PARAMETER SortBy
排序依据的内存属性,可选:WorkingSet(工作集,默认)、PrivateMemory(私有内存)、
VirtualMemory(虚拟内存)、PagedMemory(分页内存)。
.PARAMETER OutputPath
导出报告的路径,支持 .csv 或 .html 扩展名。不指定则仅输出到控制台。
.PARAMETER ComputerName
要分析的远程计算机名(需有相应权限),默认为本地计算机。
.EXAMPLE
.\makeMemRptV2.ps1
显示本地内存最高的 20 个进程及其子进程详情(含路径/服务/自启动/可关闭性)。
.EXAMPLE
.\makeMemRptV2.ps1 -Top 10 -SortBy PrivateMemory -OutputPath report.html
按私有内存排序取前 10,导出 HTML 报告。
#>
[CmdletBinding()]
param (
[ValidateRange(1, [int]::MaxValue)]
[int]$Top = 20,
[ValidateSet('WorkingSet', 'PrivateMemory', 'VirtualMemory', 'PagedMemory')]
[string]$SortBy = 'WorkingSet',
[string]$OutputPath = '进程内存分析报告.html',
[string]$ComputerName = $env:COMPUTERNAME
)
# 判断是否为本地计算机
$isLocal = ($ComputerName -eq $env:COMPUTERNAME -or $ComputerName -eq 'localhost' -or $ComputerName -eq '127.0.0.1')
# 获取系统内存摘要(仅本地支持,远程跳过)
if ($isLocal) {
try {
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
$totalMemory = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
$freeMemory = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
$usedMemory = $totalMemory - $freeMemory
$memoryPercent = [math]::Round(($usedMemory / $totalMemory) * 100, 2)
$systemInfo = [PSCustomObject]@{
'总物理内存(GB)' = $totalMemory
'已用内存(GB)' = $usedMemory
'可用内存(GB)' = $freeMemory
'使用率(%)' = $memoryPercent
}
} catch {
$systemInfo = $null
Write-Warning "无法获取本地系统内存信息: $_"
}
} else {
$systemInfo = $null
Write-Warning "远程计算机 $ComputerName 无法获取系统内存信息(需要本地 WMI/CIM 权限)"
}
# ---------- 1. 获取所有进程(统一使用 Win32_Process,本地用 DCOM,远程用 WinRM) ----------
try {
if ($isLocal) {
$allProcesses = Get-CimInstance -ClassName Win32_Process -ErrorAction Stop
} else {
$allProcesses = Get-CimInstance -ComputerName $ComputerName -ClassName Win32_Process -ErrorAction Stop
}
Write-Verbose "获取到 $($allProcesses.Count) 个进程"
} catch {
Write-Error "无法获取进程列表: $_"
exit 1
}
# ---------- 2. 获取服务映射(PID -> 服务信息列表:显示名称、服务名、是否可停止、启动模式) ----------
$serviceMap = @{} # key: PID, value: 数组 of [PSCustomObject] { DisplayName, Name, AcceptStop, StartMode }
if ($isLocal) {
try {
$services = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
$procId = $svc.ProcessId
if ($procId -and $procId -ne 0) {
if (-not $serviceMap.ContainsKey($procId)) {
$serviceMap[$procId] = @()
}
$serviceMap[$procId] += [PSCustomObject]@{
DisplayName = $svc.DisplayName
Name = $svc.Name
AcceptStop = $svc.AcceptStop
StartMode = $svc.StartMode
}
}
}
} catch {
Write-Warning "无法获取服务信息: $_"
}
}
# ---------- 3. 获取自启动项列表(仅本地,路径集合) ----------
$autoStartPaths = @()
if ($isLocal) {
# 注册表 Run 项(HKLM 和 HKCU)
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($regPath in $regPaths) {
if (Test-Path $regPath) {
$items = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
foreach ($prop in $items.PSObject.Properties) {
if ($prop.Name -notin @('PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider')) {
$value = $prop.Value
if ($value -match '^"?([^"]+\.exe)"?') {
$exePath = $matches[1]
$autoStartPaths += $exePath
} elseif ($value -match '^(\S+\.exe)') {
$autoStartPaths += $matches[1]
}
}
}
}
}
# 启动文件夹(当前用户和公共用户)
$startupFolders = @(
[Environment]::GetFolderPath('Startup'),
"$env:ALLUSERSPROFILE\Microsoft\Windows\Start Menu\Programs\Startup"
)
foreach ($folder in $startupFolders) {
if (Test-Path $folder) {
$lnkFiles = Get-ChildItem -Path $folder -Filter *.lnk -ErrorAction SilentlyContinue
foreach ($lnk in $lnkFiles) {
$shell = New-Object -ComObject WScript.Shell
try {
$shortcut = $shell.CreateShortcut($lnk.FullName)
$targetPath = $shortcut.TargetPath
if ($targetPath -and (Test-Path $targetPath)) {
$autoStartPaths += $targetPath
}
} catch {
# 忽略无法解析的快捷方式
} finally {
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
}
}
}
}
$autoStartPaths = $autoStartPaths | Select-Object -Unique
}
# ---------- 4. 构建进程映射表(PID -> 进程详情,增加路径、服务、自启动、可关闭性、建议方法) ----------
$processMap = @{}
foreach ($p in $allProcesses) {
$procId = $p.ProcessId
if ($procId -eq $null -or $procId -eq 0) { continue }
# 获取可执行路径
$exePath = $p.ExecutablePath
if ([string]::IsNullOrWhiteSpace($exePath)) { $exePath = "(不可用)" }
# 获取关联服务(显示名称列表和可停止性标志)
$servicesDisplay = ""
$servicesNames = @()
$canStopService = $false
if ($serviceMap.ContainsKey($procId)) {
$svcList = $serviceMap[$procId]
$displayNames = $svcList | ForEach-Object { $_.DisplayName }
$servicesDisplay = $displayNames -join "; "
$servicesNames = $svcList | ForEach-Object { $_.Name }
# 只要有一个服务可以停止,则认为该进程关联的服务可停止
$canStopService = ($svcList | Where-Object { $_.AcceptStop -eq $true }).Count -gt 0
}
if ([string]::IsNullOrWhiteSpace($servicesDisplay)) { $servicesDisplay = "(无)" }
# 判断是否自启动(基于路径匹配)
$isAuto = $false
if ($isLocal) {
if ($exePath -ne "(不可用)" -and $autoStartPaths -contains $exePath) {
$isAuto = $true
}
}
# 判断是否可关闭(启发式规则)
$isKillable = $false
$closeMethod = ""
# 关键系统进程黑名单(通常不应结束)
$criticalProcesses = @("System", "Idle", "csrss", "wininit", "winlogon", "services", "lsass", "smss", "svchost")
$procName = $p.Name -replace '\.exe$', ''
if ($servicesNames.Count -gt 0) {
# 有关联服务:如果服务可以停止,标记为可关闭
if ($canStopService) {
$isKillable = $true
$closeMethod = "可停止服务: 运行 'sc config <服务名> start= disabled' 禁用,然后 'net stop <服务名>' 停止"
} else {
$isKillable = $false
$closeMethod = "服务不可停止或为系统关键服务,不建议关闭"
}
} else {
# 无服务进程:非系统进程且属于用户会话(SessionId != 0)通常可结束
if ($criticalProcesses -notcontains $procName -and $p.SessionId -ne 0) {
$isKillable = $true
$closeMethod = "用户进程,可在任务管理器中结束任务"
} else {
$isKillable = $false
$closeMethod = "系统关键进程或后台服务,不建议关闭"
}
}
# 对于自启动程序,额外给出关闭方法
if ($isAuto) {
$closeMethod += " (自启动项: 使用 msconfig 或任务管理器-启动选项卡禁用)"
}
$processMap[$procId] = [PSCustomObject]@{
Name = $procName
ParentPid = $p.ParentProcessId
WorkingSetMB = [math]::Round($p.WorkingSetSize / 1MB, 2)
PrivateMemMB = [math]::Round($p.PrivatePageCount / 1MB, 2)
VirtualMemMB = [math]::Round($p.VirtualSize / 1MB, 2)
PagedMemMB = [math]::Round($p.PageFileUsage / 1MB, 2)
SortValue = $null
ExecutablePath = $exePath
Services = $servicesDisplay # 显示名称
IsAutoStart = $isAuto
IsKillable = $isKillable
CloseMethod = $closeMethod.TrimStart(';').Trim()
}
}
# 补充自启动判断:服务自动启动的影响(将自动启动的服务标记为自启动)
if ($isLocal) {
# 获取服务启动模式映射(ServiceName -> StartMode)
$serviceStartMode = @{}
try {
$servicesAll = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $servicesAll) {
$serviceStartMode[$svc.Name] = $svc.StartMode
}
} catch { }
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
if (-not $proc.IsAutoStart) {
if ($serviceMap.ContainsKey($procId)) {
foreach ($svcInfo in $serviceMap[$procId]) {
if ($serviceStartMode.ContainsKey($svcInfo.Name) -and $serviceStartMode[$svcInfo.Name] -eq "Auto") {
$proc.IsAutoStart = $true
# 如果之前没有关闭方法,补充
if ([string]::IsNullOrWhiteSpace($proc.CloseMethod)) {
$proc.CloseMethod = "服务自动启动: 使用 services.msc 将启动类型改为'手动'或'禁用'"
} else {
$proc.CloseMethod += "; 服务自动启动: 可改为手动或禁用"
}
break
}
}
}
}
}
}
# 转换 IsAutoStart 为友好字符串(显示用)
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
$proc.IsAutoStart = if ($proc.IsAutoStart) { "是" } else { "否" }
$proc.IsKillable = if ($proc.IsKillable) { "是" } else { "否" }
}
# 映射排序字段到 Win32_Process 属性
$sortFieldMap = @{
'WorkingSet' = 'WorkingSetSize'
'PrivateMemory' = 'PrivatePageCount'
'VirtualMemory' = 'VirtualSize'
'PagedMemory' = 'PageFileUsage'
}
$cimProperty = $sortFieldMap[$SortBy]
# 填充每个进程的 SortValue
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
$rawProc = $allProcesses | Where-Object { $_.ProcessId -eq $procId } | Select-Object -First 1
if ($rawProc) {
$rawValue = $rawProc.$cimProperty
$proc.SortValue = [math]::Round($rawValue / 1MB, 2)
} else {
$proc.SortValue = 0
}
}
# ---------- 5. 构建子进程映射表(父PID -> 子PID列表) ----------
$childrenMap = @{}
foreach ($procId in $processMap.Keys) {
$parentPid = $processMap[$procId].ParentPid
if ($parentPid -and $parentPid -ne 0 -and $processMap.ContainsKey($parentPid)) {
if (-not $childrenMap.ContainsKey($parentPid)) {
$childrenMap[$parentPid] = @()
}
$childrenMap[$parentPid] += $procId
}
}
# ---------- 6. 获取内存最高的 Top N 个进程作为父进程 ----------
$sortedParents = $processMap.Keys | ForEach-Object {
$proc = $processMap[$_]
[PSCustomObject]@{
PID = $_
Name = $proc.Name
SortValue = $proc.SortValue
WorkingSetMB = $proc.WorkingSetMB
PrivateMemMB = $proc.PrivateMemMB
VirtualMemMB = $proc.VirtualMemMB
PagedMemMB = $proc.PagedMemMB
ExecutablePath = $proc.ExecutablePath
Services = $proc.Services
IsAutoStart = $proc.IsAutoStart
IsKillable = $proc.IsKillable
CloseMethod = $proc.CloseMethod
}
} | Sort-Object -Property SortValue -Descending | Select-Object -First $Top
# ---------- 7. 为每个父进程收集子进程详情(包含新增字段) ----------
$reportData = @()
foreach ($parent in $sortedParents) {
$parentPid = $parent.PID
$childPids = if ($childrenMap.ContainsKey($parentPid)) { $childrenMap[$parentPid] } else { @() }
$childList = @()
$childTotalMem = 0.0
foreach ($childPid in $childPids) {
$child = $processMap[$childPid]
$childMem = $child.WorkingSetMB
$childTotalMem += $childMem
$childList += [PSCustomObject]@{
ChildName = $child.Name
ChildPID = $childPid
ChildWorkingSetMB = $childMem
ChildExecutablePath = $child.ExecutablePath
ChildServices = $child.Services
ChildIsAutoStart = $child.IsAutoStart
ChildIsKillable = $child.IsKillable
ChildCloseMethod = $child.CloseMethod
}
}
$reportData += [PSCustomObject]@{
ParentPID = $parentPid
ParentName = $parent.Name
ParentWorkingSetMB = $parent.WorkingSetMB
ParentPrivateMemMB = $parent.PrivateMemMB
ParentVirtualMemMB = $parent.VirtualMemMB
ParentPagedMemMB = $parent.PagedMemMB
ParentExecutablePath = $parent.ExecutablePath
ParentServices = $parent.Services
ParentIsAutoStart = $parent.IsAutoStart
ParentIsKillable = $parent.IsKillable
ParentCloseMethod = $parent.CloseMethod
ChildCount = $childPids.Count
ChildTotalMemMB = [math]::Round($childTotalMem, 2)
Children = $childList
}
}
# ---------- 8. 控制台输出(包含新增信息) ----------
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportTitle = "进程内存及子进程分析报告 - $ComputerName ($timestamp)"
$sortDesc = "按 $SortBy (MB) 降序排列,显示 Top $Top"
Write-Host "`n========== $reportTitle ==========" -ForegroundColor Cyan
if ($systemInfo) {
Write-Host "系统内存摘要:" -ForegroundColor Yellow
$systemInfo | Format-List
}
Write-Host "排序方式: $sortDesc" -ForegroundColor Yellow
Write-Host "`n父进程列表 (内存占用最高的 $Top 个):`n" -ForegroundColor Green
foreach ($item in $reportData) {
Write-Host ">>> 父进程: $($item.ParentName) (PID: $($item.ParentPID))" -ForegroundColor Magenta
Write-Host " 自身内存: WS=$($item.ParentWorkingSetMB) MB, 私有=$($item.ParentPrivateMemMB) MB, 虚拟=$($item.ParentVirtualMemMB) MB" -ForegroundColor Gray
Write-Host " 路径: $($item.ParentExecutablePath)" -ForegroundColor Gray
Write-Host " 关联服务: $($item.ParentServices)" -ForegroundColor Gray
Write-Host " 自启动: $($item.ParentIsAutoStart)" -ForegroundColor Gray
Write-Host " 是否可关闭: $($item.ParentIsKillable)" -ForegroundColor Gray
Write-Host " 关闭/禁用建议: $($item.ParentCloseMethod)" -ForegroundColor Gray
Write-Host " 子进程数量: $($item.ChildCount), 子进程总内存: $($item.ChildTotalMemMB) MB" -ForegroundColor Gray
if ($item.ChildCount -gt 0) {
Write-Host " 子进程详情:" -ForegroundColor Gray
foreach ($child in $item.Children) {
Write-Host " - $($child.ChildName) (PID:$($child.ChildPID)) - $($child.ChildWorkingSetMB) MB" -ForegroundColor DarkGray
Write-Host " 路径: $($child.ChildExecutablePath)" -ForegroundColor DarkGray
Write-Host " 服务: $($child.ChildServices)" -ForegroundColor DarkGray
Write-Host " 自启动: $($child.ChildIsAutoStart)" -ForegroundColor DarkGray
Write-Host " 可关闭: $($child.ChildIsKillable)" -ForegroundColor DarkGray
Write-Host " 建议: $($child.ChildCloseMethod)" -ForegroundColor DarkGray
}
} else {
Write-Host " 无子进程" -ForegroundColor DarkGray
}
Write-Host ""
}
# ---------- 9. 导出报告(CSV 或 HTML,增加新列) ----------
if ($OutputPath) {
$directory = Split-Path $OutputPath -Parent
if ($directory -and -not (Test-Path $directory)) {
New-Item -Path $directory -ItemType Directory -Force | Out-Null
}
$extension = [System.IO.Path]::GetExtension($OutputPath).ToLower()
try {
if ($extension -eq '.csv') {
$csvRows = @()
foreach ($item in $reportData) {
if ($item.Children.Count -eq 0) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程WS_MB = $item.ParentWorkingSetMB
父进程私有_MB = $item.ParentPrivateMemMB
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $null
子进程名称 = $null
子进程WS_MB = $null
子进程路径 = $null
子进程服务 = $null
子进程自启动 = $null
子进程可关闭 = $null
子进程建议 = $null
}
} else {
foreach ($child in $item.Children) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程WS_MB = $item.ParentWorkingSetMB
父进程私有_MB = $item.ParentPrivateMemMB
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $child.ChildPID
子进程名称 = $child.ChildName
子进程WS_MB = $child.ChildWorkingSetMB
子进程路径 = $child.ChildExecutablePath
子进程服务 = $child.ChildServices
子进程自启动 = $child.ChildIsAutoStart
子进程可关闭 = $child.ChildIsKillable
子进程建议 = $child.ChildCloseMethod
}
}
}
}
$csvRows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "`n报告已导出为 CSV: $OutputPath" -ForegroundColor Green
} elseif ($extension -eq '.html') {
$html = @"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>$reportTitle</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #2c3e50; }
h2 { color: #34495e; border-bottom: 1px solid #ccc; }
.summary { background-color: #eef; padding: 10px; margin-bottom: 20px; border-radius: 5px; }
.parent-table { border-collapse: collapse; width: 100%; margin-bottom: 30px; }
.parent-table th, .parent-table td { border: 1px solid #ddd; padding: 8px; text-align: left; vertical-align: top; }
.parent-table th { background-color: #4CAF50; color: white; }
.child-table { width: 100%; border-collapse: collapse; margin-top: 5px; }
.child-table th, .child-table td { border: 1px solid #ccc; padding: 4px; font-size: 0.9em; }
.child-table th { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>$reportTitle</h1>
<div class='summary'>
"@
if ($systemInfo) {
$html += "<ul>"
$systemInfo.PSObject.Properties | ForEach-Object {
$html += "<li><strong>$($_.Name):</strong> $($_.Value)</li>"
}
$html += "</ul>"
} else {
$html += "<p>无法获取系统内存信息</p>"
}
$html += "<p><strong>排序依据:</strong> $sortDesc</p>"
$html += "</div>"
foreach ($item in $reportData) {
$html += "<h2>父进程: $($item.ParentName) (PID $($item.ParentPID))</h2>"
$html += "<table class='parent-table'>"
$html += "<tr><th style='width:150px'>属性</th><th>值</th></tr>"
$html += "<tr>。<工作集内存 (MB)</td>。<td>$($item.ParentWorkingSetMB)</td></tr>"
$html += "<tr>。<私有内存 (MB)</td>。<td>$($item.ParentPrivateMemMB)</td></tr>"
$html += "<tr>。<虚拟内存 (MB)</td>。<td>$($item.ParentVirtualMemMB)</td></tr>"
$html += "<tr>。<分页内存 (MB)</td>。<td>$($item.ParentPagedMemMB)</td></tr>"
$html += "<tr>。<路径</th><td>$($item.ParentExecutablePath)</td></tr>"
$html += "<tr>。<关联服务</th><td>$($item.ParentServices)</td></tr>"
$html += "<tr>。<自启动</th><td>$($item.ParentIsAutoStart)</td></tr>"
$html += "<tr>。<可关闭</th><td>$($item.ParentIsKillable)</td></tr>"
$html += "<tr>。<关闭/禁用建议</th><td>$($item.ParentCloseMethod)</td></tr>"
$html += "<tr>。<子进程数量</th><td>$($item.ChildCount)</td></tr>"
$html += "<tr>。<子进程总内存 (MB)</th><td>$($item.ChildTotalMemMB)</td></tr>"
$html += "</table>"
if ($item.Children.Count -gt 0) {
$html += "<h3>子进程列表</h3>"
$html += "<table class='child-table'>"
$html += "<tr><th>子进程名称</th><th>PID</th><th>工作集(MB)</th><th>路径</th><th>关联服务</th><th>自启动</th><th>可关闭</th><th>建议</th></tr>"
foreach ($child in $item.Children) {
$html += "<tr>"
$html += "<td>$($child.ChildName)</td>"
$html += "<td>$($child.ChildPID)</td>"
$html += "<td>$($child.ChildWorkingSetMB)</td>"
$html += "<td>$($child.ChildExecutablePath)</td>"
$html += "<td>$($child.ChildServices)</td>"
$html += "<td>$($child.ChildIsAutoStart)</td>"
$html += "<td>$($child.ChildIsKillable)</td>"
$html += "<td>$($child.ChildCloseMethod)</td>"
$html += "</tr>"
}
$html += "</table>"
} else {
$html += "<p><em>无子进程</em></p>"
}
}
$html += "</body></html>"
$html | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "`n报告已导出为 HTML: $OutputPath" -ForegroundColor Green
} else {
Write-Warning "不支持的文件扩展名 '$extension',仅支持 .csv 或 .html。"
}
} catch {
Write-Error "导出失败: $_"
}
}
Write-Host "`n分析完成。" -ForegroundColor Cyan
pause
2.处理器分析工具:
说明:
- makeNetworkRptV3.ps1 – 进程网络分析报告
该脚本分析本地计算机进程的网络活动(远程仅显示进程列表),按 TCP 连接数、UDP 端点数或总端点数排序,取 Top N 个父进程,并展示每个父进程的直接子进程网络统计。
对网络活动 >0 的进程,显示访问最多的前 10 个远程地址(IP:端口),并尝试反向解析域名。
支持导出 CSV/HTML。
主要参数:-Top(默认20)、-SortBy(TcpConnections/UdpEndpoints/TotalEndpoints)、-OutputPath、-ComputerName。
示例:.\makeNetworkRptV3.ps1 -Top 15 -SortBy TotalEndpoints
适用于监控异常外连进程、分析网络流量来源。
代码:
点击查看代码
<#
.SYNOPSIS
生成进程 CPU 使用分析报告,并显示每个进程的子进程 CPU 占用(Top N)
扩展功能:显示进程路径、关联服务(显示名称)、是否可关闭、关闭/禁用方法
.DESCRIPTION
分析进程 CPU 使用率(本地为百分比,远程为累计 CPU 秒数),按指定字段排序,取前 N 个进程。
对每个进程,列出其直接子进程的名称、PID、CPU 信息、路径、关联服务(显示名称)、
是否可关闭以及建议的关闭/禁用方法。
支持导出 CSV/HTML,自动识别本地计算机避免 WinRM 依赖。
本地模式下会通过两次采样计算 CPU 百分比(采样间隔 0.5 秒)。
.PARAMETER Top
显示 CPU 占用最高的 N 个父进程,默认 20。
.PARAMETER SortBy
排序依据的 CPU 属性,本地模式可选:'CpuPercent'(CPU 百分比,默认)、'CpuTotalSeconds'(累计 CPU 秒数);
远程模式仅支持 'CpuTotalSeconds'。
.PARAMETER OutputPath
导出报告的路径,支持 .csv 或 .html 扩展名。不指定则仅输出到控制台。
.PARAMETER ComputerName
要分析的远程计算机名(需有相应权限),默认为本地计算机。
.PARAMETER SampleInterval
本地模式下计算 CPU 百分比时的采样间隔(秒),默认 0.5。注意:系统总 CPU 使用率采样间隔会自动提升到至少 1 秒。
.EXAMPLE
.\makeCpuRptV3.ps1
显示本地 CPU 占用最高的 20 个进程及其子进程详情。
.EXAMPLE
.\makeCpuRptV3.ps1 -Top 10 -SortBy CpuTotalSeconds -OutputPath report.html
按累计 CPU 时间排序取前 10,导出 HTML 报告。
.EXAMPLE
.\makeCpuRptV3.ps1 -ComputerName Server01 -Top 15
分析远程计算机 Server01 的 CPU 占用(显示累计 CPU 时间)
#>
[CmdletBinding()]
param (
[ValidateRange(1, [int]::MaxValue)]
[int]$Top = 20,
[ValidateSet('CpuPercent', 'CpuTotalSeconds')]
[string]$SortBy = 'CpuPercent',
[string]$OutputPath = '进程CPU分析报告.html',
[string]$ComputerName = $env:COMPUTERNAME,
[ValidateRange(0.2, 5.0)]
[double]$SampleInterval = 0.5
)
# 判断是否为本地计算机
$isLocal = ($ComputerName -eq $env:COMPUTERNAME -or $ComputerName -eq 'localhost' -or $ComputerName -eq '127.0.0.1')
# 远程模式下强制使用 CpuTotalSeconds(因为无法轻松获得百分比)
if (-not $isLocal -and $SortBy -eq 'CpuPercent') {
Write-Warning "远程计算机 $ComputerName 不支持按 CPU 百分比排序,已自动切换为按累计 CPU 秒数排序"
$SortBy = 'CpuTotalSeconds'
}
# 获取系统 CPU 信息(仅本地支持)
$systemCpuInfo = $null
if ($isLocal) {
try {
# 获取 CPU 核心数
$cpuCores = (Get-CimInstance -ClassName Win32_ComputerSystem).NumberOfLogicalProcessors
# 获取总的 CPU 使用率(采样两次),注意 Get-Counter 的 -SampleInterval 必须 >=1
$effectiveInterval = [math]::Max(1.0, $SampleInterval)
$counter = Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval $effectiveInterval -MaxSamples 2 -ErrorAction Stop
$totalCpuPercent = [math]::Round($counter[1].CounterSamples[0].CookedValue, 2)
$systemCpuInfo = [PSCustomObject]@{
'逻辑处理器数' = $cpuCores
'总CPU使用率(%)' = $totalCpuPercent
}
} catch {
$systemCpuInfo = $null
Write-Warning "无法获取系统 CPU 信息: $_"
}
} else {
Write-Warning "远程计算机 $ComputerName 无法获取系统 CPU 信息"
}
# ---------- 1. 获取所有进程(统一使用 Win32_Process) ----------
try {
if ($isLocal) {
$allProcesses = Get-CimInstance -ClassName Win32_Process -ErrorAction Stop
} else {
$allProcesses = Get-CimInstance -ComputerName $ComputerName -ClassName Win32_Process -ErrorAction Stop
}
Write-Verbose "获取到 $($allProcesses.Count) 个进程"
} catch {
Write-Error "无法获取进程列表: $_"
exit 1
}
# ---------- 2. 获取服务映射(PID -> 服务信息) ----------
$serviceMap = @{}
if ($isLocal) {
try {
$services = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
$procId = $svc.ProcessId
if ($procId -and $procId -ne 0) {
if (-not $serviceMap.ContainsKey($procId)) {
$serviceMap[$procId] = @()
}
$serviceMap[$procId] += [PSCustomObject]@{
DisplayName = $svc.DisplayName
Name = $svc.Name
AcceptStop = $svc.AcceptStop
StartMode = $svc.StartMode
}
}
}
} catch {
Write-Warning "无法获取服务信息: $_"
}
}
# ---------- 3. 获取自启动项列表(仅本地) ----------
$autoStartPaths = @()
if ($isLocal) {
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($regPath in $regPaths) {
if (Test-Path $regPath) {
$items = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
foreach ($prop in $items.PSObject.Properties) {
if ($prop.Name -notin @('PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider')) {
$value = $prop.Value
if ($value -match '^"?([^"]+\.exe)"?') {
$exePath = $matches[1]
$autoStartPaths += $exePath
} elseif ($value -match '^(\S+\.exe)') {
$autoStartPaths += $matches[1]
}
}
}
}
}
$startupFolders = @(
[Environment]::GetFolderPath('Startup'),
"$env:ALLUSERSPROFILE\Microsoft\Windows\Start Menu\Programs\Startup"
)
foreach ($folder in $startupFolders) {
if (Test-Path $folder) {
$lnkFiles = Get-ChildItem -Path $folder -Filter *.lnk -ErrorAction SilentlyContinue
foreach ($lnk in $lnkFiles) {
$shell = New-Object -ComObject WScript.Shell
try {
$shortcut = $shell.CreateShortcut($lnk.FullName)
$targetPath = $shortcut.TargetPath
if ($targetPath -and (Test-Path $targetPath)) {
$autoStartPaths += $targetPath
}
} catch {
# 忽略无法解析的快捷方式
} finally {
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
}
}
}
}
$autoStartPaths = $autoStartPaths | Select-Object -Unique
}
# ---------- 4. CPU 采样:本地需要两次获取 CPU 时间 ----------
# 对于每个进程,我们需要记录:PID, KernelModeTime, UserModeTime, CreateTime (用于避免进程重生)
if ($isLocal -and $SortBy -eq 'CpuPercent') {
# 第一次采样
$firstSample = @{}
foreach ($p in $allProcesses) {
$currentPid = $p.ProcessId
if ($currentPid -eq 0 -or $currentPid -eq $null) { continue }
$firstSample[$currentPid] = @{
KernelTime = $p.KernelModeTime
UserTime = $p.UserModeTime
CreateTime = $p.CreationDate
}
}
Start-Sleep -Seconds $SampleInterval
# 第二次采样(重新获取进程列表,防止进程退出)
try {
if ($isLocal) {
$secondProcesses = Get-CimInstance -ClassName Win32_Process -ErrorAction Stop
} else {
$secondProcesses = Get-CimInstance -ComputerName $ComputerName -ClassName Win32_Process -ErrorAction Stop
}
} catch {
Write-Error "第二次采样获取进程失败: $_"
exit 1
}
$secondSample = @{}
foreach ($p in $secondProcesses) {
$currentPid = $p.ProcessId
if ($currentPid -eq 0 -or $currentPid -eq $null) { continue }
$secondSample[$currentPid] = @{
KernelTime = $p.KernelModeTime
UserTime = $p.UserModeTime
CreateTime = $p.CreationDate
}
}
}
# ---------- 5. 构建进程映射表 ----------
$processMap = @{}
# 获取 CPU 核心数用于百分比计算
$cpuCores = if ($isLocal) { (Get-CimInstance -ClassName Win32_ComputerSystem).NumberOfLogicalProcessors } else { 1 }
foreach ($p in $allProcesses) {
$procId = $p.ProcessId
if ($procId -eq $null -or $procId -eq 0) { continue }
# 可执行路径
$exePath = $p.ExecutablePath
if ([string]::IsNullOrWhiteSpace($exePath)) { $exePath = "(不可用)" }
# 关联服务
$servicesDisplay = ""
$servicesNames = @()
$canStopService = $false
if ($serviceMap.ContainsKey($procId)) {
$svcList = $serviceMap[$procId]
$displayNames = $svcList | ForEach-Object { $_.DisplayName }
$servicesDisplay = $displayNames -join "; "
$servicesNames = $svcList | ForEach-Object { $_.Name }
$canStopService = ($svcList | Where-Object { $_.AcceptStop -eq $true }).Count -gt 0
}
if ([string]::IsNullOrWhiteSpace($servicesDisplay)) { $servicesDisplay = "(无)" }
# 自启动判断
$isAuto = $false
if ($isLocal -and $exePath -ne "(不可用)" -and $autoStartPaths -contains $exePath) {
$isAuto = $true
}
# CPU 相关值
$cpuPercent = $null
$cpuTotalSeconds = $null
if ($isLocal -and $SortBy -eq 'CpuPercent' -and $firstSample.ContainsKey($procId) -and $secondSample.ContainsKey($procId)) {
$first = $firstSample[$procId]
$second = $secondSample[$procId]
# 确保进程未重启(创建时间相同)
if ($first.CreateTime -eq $second.CreateTime) {
$deltaKernel = [double]($second.KernelTime - $first.KernelTime)
$deltaUser = [double]($second.UserTime - $first.UserTime)
$deltaTotal = $deltaKernel + $deltaUser
# 时间单位:100纳秒,转换为秒
$deltaSeconds = $deltaTotal / 1e7
if ($deltaSeconds -gt 0) {
$cpuPercentRaw = ($deltaTotal / ($SampleInterval * 1e7)) * 100 / $cpuCores
$cpuPercent = [math]::Round($cpuPercentRaw, 2)
$cpuTotalSeconds = [math]::Round($p.UserModeTime / 1e7 + $p.KernelModeTime / 1e7, 2)
} else {
$cpuPercent = 0.0
$cpuTotalSeconds = [math]::Round($p.UserModeTime / 1e7 + $p.KernelModeTime / 1e7, 2)
}
} else {
# 进程已重启,无法计算百分比,使用累计时间
$cpuPercent = $null
$cpuTotalSeconds = [math]::Round($p.UserModeTime / 1e7 + $p.KernelModeTime / 1e7, 2)
}
} else {
# 远程模式或不计算百分比:仅累计秒数
$cpuTotalSeconds = [math]::Round(($p.UserModeTime + $p.KernelModeTime) / 1e7, 2)
if ($isLocal -and $SortBy -eq 'CpuPercent') {
$cpuPercent = $null # 无法计算,后续排序时会回退到总秒数
}
}
# 可关闭性判断(与内存脚本逻辑一致)
$procName = $p.Name -replace '\.exe$', ''
$criticalProcesses = @("System", "Idle", "csrss", "wininit", "winlogon", "services", "lsass", "smss", "svchost")
$isKillable = $false
$closeMethod = ""
if ($servicesNames.Count -gt 0) {
if ($canStopService) {
$isKillable = $true
$closeMethod = "可停止服务: 运行 'sc config <服务名> start= disabled' 禁用,然后 'net stop <服务名>' 停止"
} else {
$isKillable = $false
$closeMethod = "服务不可停止或为系统关键服务,不建议关闭"
}
} else {
if ($criticalProcesses -notcontains $procName -and $p.SessionId -ne 0) {
$isKillable = $true
$closeMethod = "用户进程,可在任务管理器中结束任务"
} else {
$isKillable = $false
$closeMethod = "系统关键进程或后台服务,不建议关闭"
}
}
if ($isAuto) {
$closeMethod += " (自启动项: 使用 msconfig 或任务管理器-启动选项卡禁用)"
}
# 存储(修正:添加 SortValue 属性以便后续赋值)
$processMap[$procId] = [PSCustomObject]@{
Name = $procName
ParentPid = $p.ParentProcessId
CpuPercent = $cpuPercent
CpuTotalSeconds = $cpuTotalSeconds
ExecutablePath = $exePath
Services = $servicesDisplay
IsAutoStart = $isAuto
IsKillable = $isKillable
CloseMethod = $closeMethod.TrimStart(';').Trim()
SortValue = $null # 新增属性,用于排序
}
}
# 补充自启动判断:服务自动启动的影响
if ($isLocal) {
$serviceStartMode = @{}
try {
$servicesAll = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $servicesAll) {
$serviceStartMode[$svc.Name] = $svc.StartMode
}
} catch { }
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
if (-not $proc.IsAutoStart -and $serviceMap.ContainsKey($procId)) {
foreach ($svcInfo in $serviceMap[$procId]) {
if ($serviceStartMode.ContainsKey($svcInfo.Name) -and $serviceStartMode[$svcInfo.Name] -eq "Auto") {
$proc.IsAutoStart = $true
if ([string]::IsNullOrWhiteSpace($proc.CloseMethod)) {
$proc.CloseMethod = "服务自动启动: 使用 services.msc 将启动类型改为'手动'或'禁用'"
} else {
$proc.CloseMethod += "; 服务自动启动: 可改为手动或禁用"
}
break
}
}
}
}
}
# 转换 IsAutoStart, IsKillable 为字符串
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
$proc.IsAutoStart = if ($proc.IsAutoStart) { "是" } else { "否" }
$proc.IsKillable = if ($proc.IsKillable) { "是" } else { "否" }
}
# 确定排序字段的值
if ($SortBy -eq 'CpuPercent' -and $isLocal) {
# 对于无法计算百分比的进程(如重启过),使用累计时间作为后备(但百分比显示为空,排序时用0)
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
if ($proc.CpuPercent -eq $null) {
$proc.SortValue = $proc.CpuTotalSeconds
} else {
$proc.SortValue = $proc.CpuPercent
}
}
} else {
foreach ($procId in $processMap.Keys) {
$processMap[$procId].SortValue = $processMap[$procId].CpuTotalSeconds
}
}
# ---------- 6. 构建子进程映射 ----------
$childrenMap = @{}
foreach ($procId in $processMap.Keys) {
$parentPid = $processMap[$procId].ParentPid
if ($parentPid -and $parentPid -ne 0 -and $processMap.ContainsKey($parentPid)) {
if (-not $childrenMap.ContainsKey($parentPid)) {
$childrenMap[$parentPid] = @()
}
$childrenMap[$parentPid] += $procId
}
}
# ---------- 7. 获取 CPU 占用最高的 Top N 个父进程 ----------
$sortedParents = $processMap.Keys | ForEach-Object {
$proc = $processMap[$_]
[PSCustomObject]@{
PID = $_
Name = $proc.Name
SortValue = $proc.SortValue
CpuPercent = $proc.CpuPercent
CpuTotalSeconds = $proc.CpuTotalSeconds
ExecutablePath = $proc.ExecutablePath
Services = $proc.Services
IsAutoStart = $proc.IsAutoStart
IsKillable = $proc.IsKillable
CloseMethod = $proc.CloseMethod
}
} | Sort-Object -Property SortValue -Descending | Select-Object -First $Top
# ---------- 8. 为每个父进程收集子进程详情 ----------
$reportData = @()
foreach ($parent in $sortedParents) {
$parentPid = $parent.PID
$childPids = if ($childrenMap.ContainsKey($parentPid)) { $childrenMap[$parentPid] } else { @() }
$childList = @()
$childTotalCpu = 0.0
foreach ($childPid in $childPids) {
$child = $processMap[$childPid]
$childCpuValue = if ($SortBy -eq 'CpuPercent' -and $child.CpuPercent -ne $null) { $child.CpuPercent } else { $child.CpuTotalSeconds }
$childTotalCpu += $childCpuValue
$childList += [PSCustomObject]@{
ChildName = $child.Name
ChildPID = $childPid
ChildCpuValue = $childCpuValue
ChildCpuPercent = $child.CpuPercent
ChildCpuTotalSeconds = $child.CpuTotalSeconds
ChildExecutablePath = $child.ExecutablePath
ChildServices = $child.Services
ChildIsAutoStart = $child.IsAutoStart
ChildIsKillable = $child.IsKillable
ChildCloseMethod = $child.CloseMethod
}
}
$reportData += [PSCustomObject]@{
ParentPID = $parentPid
ParentName = $parent.Name
ParentCpuValue = $parent.SortValue
ParentCpuPercent = $parent.CpuPercent
ParentCpuTotalSeconds = $parent.CpuTotalSeconds
ParentExecutablePath = $parent.ExecutablePath
ParentServices = $parent.Services
ParentIsAutoStart = $parent.IsAutoStart
ParentIsKillable = $parent.IsKillable
ParentCloseMethod = $parent.CloseMethod
ChildCount = $childPids.Count
ChildTotalCpu = [math]::Round($childTotalCpu, 2)
Children = $childList
}
}
# ---------- 9. 控制台输出 ----------
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportTitle = "进程 CPU 分析报告 - $ComputerName ($timestamp)"
$sortDesc = if ($SortBy -eq 'CpuPercent') { "按 CPU 使用率 (%) 降序,显示 Top $Top" } else { "按累计 CPU 时间 (秒) 降序,显示 Top $Top" }
Write-Host "`n========== $reportTitle ==========" -ForegroundColor Cyan
if ($systemCpuInfo) {
Write-Host "系统 CPU 摘要:" -ForegroundColor Yellow
$systemCpuInfo | Format-List
}
Write-Host "排序方式: $sortDesc" -ForegroundColor Yellow
Write-Host "`n父进程列表 (CPU 占用最高的 $Top 个):`n" -ForegroundColor Green
foreach ($item in $reportData) {
$cpuUnit = if ($SortBy -eq 'CpuPercent') { "%" } else { "秒" }
Write-Host ">>> 父进程: $($item.ParentName) (PID: $($item.ParentPID))" -ForegroundColor Magenta
if ($SortBy -eq 'CpuPercent' -and $item.ParentCpuPercent -ne $null) {
Write-Host " CPU 使用率: $($item.ParentCpuPercent)% (累计时间: $($item.ParentCpuTotalSeconds) 秒)" -ForegroundColor Gray
} else {
Write-Host " CPU 累计时间: $($item.ParentCpuTotalSeconds) 秒" -ForegroundColor Gray
}
Write-Host " 路径: $($item.ParentExecutablePath)" -ForegroundColor Gray
Write-Host " 关联服务: $($item.ParentServices)" -ForegroundColor Gray
Write-Host " 自启动: $($item.ParentIsAutoStart)" -ForegroundColor Gray
Write-Host " 是否可关闭: $($item.ParentIsKillable)" -ForegroundColor Gray
Write-Host " 关闭/禁用建议: $($item.ParentCloseMethod)" -ForegroundColor Gray
Write-Host " 子进程数量: $($item.ChildCount), 子进程总 CPU: $($item.ChildTotalCpu) $cpuUnit" -ForegroundColor Gray
if ($item.ChildCount -gt 0) {
Write-Host " 子进程详情:" -ForegroundColor Gray
foreach ($child in $item.Children) {
if ($SortBy -eq 'CpuPercent' -and $child.ChildCpuPercent -ne $null) {
Write-Host " - $($child.ChildName) (PID:$($child.ChildPID)) - $($child.ChildCpuPercent)% (累计 $($child.ChildCpuTotalSeconds) 秒)" -ForegroundColor DarkGray
} else {
Write-Host " - $($child.ChildName) (PID:$($child.ChildPID)) - $($child.ChildCpuTotalSeconds) 秒" -ForegroundColor DarkGray
}
Write-Host " 路径: $($child.ChildExecutablePath)" -ForegroundColor DarkGray
Write-Host " 服务: $($child.ChildServices)" -ForegroundColor DarkGray
Write-Host " 自启动: $($child.ChildIsAutoStart)" -ForegroundColor DarkGray
Write-Host " 可关闭: $($child.ChildIsKillable)" -ForegroundColor DarkGray
Write-Host " 建议: $($child.ChildCloseMethod)" -ForegroundColor DarkGray
}
} else {
Write-Host " 无子进程" -ForegroundColor DarkGray
}
Write-Host ""
}
# ---------- 10. 导出报告(CSV / HTML) ----------
if ($OutputPath) {
$directory = Split-Path $OutputPath -Parent
if ($directory -and -not (Test-Path $directory)) {
New-Item -Path $directory -ItemType Directory -Force | Out-Null
}
$extension = [System.IO.Path]::GetExtension($OutputPath).ToLower()
try {
if ($extension -eq '.csv') {
$csvRows = @()
foreach ($item in $reportData) {
if ($item.Children.Count -eq 0) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程CPU值 = $item.ParentCpuValue
父进程CPU百分比 = $item.ParentCpuPercent
父进程CPU总秒数 = $item.ParentCpuTotalSeconds
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $null
子进程名称 = $null
子进程CPU值 = $null
子进程CPU百分比 = $null
子进程CPU秒数 = $null
子进程路径 = $null
子进程服务 = $null
子进程自启动 = $null
子进程可关闭 = $null
子进程建议 = $null
}
} else {
foreach ($child in $item.Children) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程CPU值 = $item.ParentCpuValue
父进程CPU百分比 = $item.ParentCpuPercent
父进程CPU总秒数 = $item.ParentCpuTotalSeconds
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $child.ChildPID
子进程名称 = $child.ChildName
子进程CPU值 = $child.ChildCpuValue
子进程CPU百分比 = $child.ChildCpuPercent
子进程CPU秒数 = $child.ChildCpuTotalSeconds
子进程路径 = $child.ChildExecutablePath
子进程服务 = $child.ChildServices
子进程自启动 = $child.ChildIsAutoStart
子进程可关闭 = $child.ChildIsKillable
子进程建议 = $child.ChildCloseMethod
}
}
}
}
$csvRows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "`n报告已导出为 CSV: $OutputPath" -ForegroundColor Green
} elseif ($extension -eq '.html') {
$html = @"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>$reportTitle</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #2c3e50; }
h2 { color: #34495e; border-bottom: 1px solid #ccc; }
.summary { background-color: #eef; padding: 10px; margin-bottom: 20px; border-radius: 5px; }
.parent-table { border-collapse: collapse; width: 100%; margin-bottom: 30px; }
.parent-table th, .parent-table td { border: 1px solid #ddd; padding: 8px; text-align: left; vertical-align: top; }
.parent-table th { background-color: #4CAF50; color: white; }
.child-table { width: 100%; border-collapse: collapse; margin-top: 5px; }
.child-table th, .child-table td { border: 1px solid #ccc; padding: 4px; font-size: 0.9em; }
.child-table th { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>$reportTitle</h1>
<div class='summary'>
"@
if ($systemCpuInfo) {
$html += "<ul>"
$systemCpuInfo.PSObject.Properties | ForEach-Object {
$html += "<li><strong>$($_.Name):</strong> $($_.Value)</li>"
}
$html += "</ul>"
} else {
$html += "<p>无法获取系统 CPU 信息</p>"
}
$html += "<p><strong>排序依据:</strong> $sortDesc</p>"
$html += "</div>"
foreach ($item in $reportData) {
$html += "<h2>父进程: $($item.ParentName) (PID $($item.ParentPID))</h2>"
$html += "<table class='parent-table'>"
$html += "<tr><th style='width:150px'>属性</th><th>值</th></tr>"
if ($SortBy -eq 'CpuPercent' -and $item.ParentCpuPercent -ne $null) {
$html += "<tr>。<CPU 使用率 (%)</th><td>$($item.ParentCpuPercent)%</td>树"
$html += "<tr>。<累计 CPU 时间 (秒)</th><td>$($item.ParentCpuTotalSeconds)</td>树"
} else {
$html += "<tr>。<累计 CPU 时间 (秒)</th><td>$($item.ParentCpuTotalSeconds)</td>树"
}
$html += "<tr>。<路径</th><td>$($item.ParentExecutablePath)</td>树"
$html += "<tr>。<关联服务</th><td>$($item.ParentServices)</td>树"
$html += "<tr>。<自启动</th><td>$($item.ParentIsAutoStart)</td>树"
$html += "<tr>。<可关闭</th><td>$($item.ParentIsKillable)</td>树"
$html += "<tr>。<关闭/禁用建议</th><td>$($item.ParentCloseMethod)</td>树"
$html += "<tr>。<子进程数量</th><td>$($item.ChildCount)</td>树"
$html += "<tr>。<子进程总 CPU</th><td>$($item.ChildTotalCpu) $(if ($SortBy -eq 'CpuPercent'){"%"} else {"秒"})</td>树"
$html += "</table>"
if ($item.Children.Count -gt 0) {
$html += "<h3>子进程列表</h3>"
$html += "<table class='child-table'>"
$html += "<tr><th>子进程名称</th><th>PID</th><th>CPU值</th><th>CPU百分比</th><th>CPU秒数</th><th>路径</th><th>关联服务</th><th>自启动</th><th>可关闭</th><th>建议</th>树"
foreach ($child in $item.Children) {
$html += "<tr>"
$html += "<td>$($child.ChildName)</td>"
$html += "<td>$($child.ChildPID)</td>"
$html += "<td>$($child.ChildCpuValue)</td>"
$html += "<td>$(if ($child.ChildCpuPercent -ne $null) {"$($child.ChildCpuPercent)%"} else {"-"})</td>"
$html += "<td>$($child.ChildCpuTotalSeconds)</td>"
$html += "<td>$($child.ChildExecutablePath)</td>"
$html += "<td>$($child.ChildServices)</td>"
$html += "<td>$($child.ChildIsAutoStart)</td>"
$html += "<td>$($child.ChildIsKillable)</td>"
$html += "<td>$($child.ChildCloseMethod)</td>"
$html += "</tr>"
}
$html += "</table>"
} else {
$html += "<p><em>无子进程</em></p>"
}
}
$html += "</body></html>"
$html | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "`n报告已导出为 HTML: $OutputPath" -ForegroundColor Green
} else {
Write-Warning "不支持的文件扩展名 '$extension',仅支持 .csv 或 .html。"
}
} catch {
Write-Error "导出失败: $_"
}
}
Write-Host "`n分析完成。" -ForegroundColor Cyan
pause
3.网络分析工具:
说明:
- makeCpuRptV3.ps1 – 进程 CPU 分析报告
该脚本分析本地或远程计算机的进程 CPU 占用。本地通过两次采样(间隔可调)计算 CPU 百分比,远程显示累计 CPU 秒数。按指定字段排序取 Top N 个父进程,并展示子进程 CPU 详情。
包含进程路径、关联服务、自启动、可关闭性及关闭建议。
支持导出 CSV/HTML。
主要参数:-Top(默认20)、-SortBy(CpuPercent/CpuTotalSeconds)、-SampleInterval(本地百分比采样间隔,默认0.5秒)、-OutputPath、-ComputerName。
示例:.\makeCpuRptV3.ps1 -Top 10 -SortBy CpuPercent -SampleInterval 1
用于定位高 CPU 消耗进程及其子进程,辅助性能调优。
代码:
点击查看代码
<#
.SYNOPSIS
生成进程网络使用分析报告,并显示每个进程的子进程网络占用(Top N)
扩展功能:显示进程路径、关联服务(显示名称)、是否可关闭、关闭/禁用方法,
对网络活动大于0的进程,显示其访问最多的前10个远程地址(IP:端口),
并尽量显示对应的域名。
.DESCRIPTION
分析进程的 TCP 连接数、UDP 端点数,按指定字段排序,取前 N 个进程。
对每个进程,列出其直接子进程的名称、PID、网络统计、路径、关联服务(显示名称)、
是否可关闭以及建议的关闭/禁用方法。
支持导出 CSV/HTML,自动识别本地计算机避免 WinRM 依赖。
注意:网络统计仅支持本地计算机,远程计算机仅显示基础进程信息(无网络数据)。
.PARAMETER Top
显示网络活动最高的 N 个父进程,默认 20。
.PARAMETER SortBy
排序依据的网络属性,可选:'TcpConnections'(TCP 连接数,默认)、'UdpEndpoints'(UDP 端点数)、
'TotalEndpoints'(总端点 = TCP+UDP)。
.PARAMETER OutputPath
导出报告的路径,支持 .csv 或 .html 扩展名。不指定则仅输出到控制台。
.PARAMETER ComputerName
要分析的远程计算机名(需有相应权限),默认为本地计算机。远程模式仅显示进程列表,无网络统计。
.EXAMPLE
.\makeNetworkRptV3.ps1
显示本地网络活动最高的 20 个进程及其子进程详情,并展示每个进程访问最多的10个远程地址(含域名)。
.EXAMPLE
.\makeNetworkRptV3.ps1 -Top 10 -SortBy TotalEndpoints -OutputPath report.html
按总端点数排序取前 10,导出 HTML 报告。
.EXAMPLE
.\makeNetworkRptV3.ps1 -ComputerName Server01 -Top 15
分析远程计算机 Server01 的进程列表(无网络统计)
#>
[CmdletBinding()]
param (
[ValidateRange(1, [int]::MaxValue)]
[int]$Top = 20,
[ValidateSet('TcpConnections', 'UdpEndpoints', 'TotalEndpoints')]
[string]$SortBy = 'TcpConnections',
[string]$OutputPath = '进程网络分析报告.html',
[string]$ComputerName = $env:COMPUTERNAME
)
# 判断是否为本地计算机
$isLocal = ($ComputerName -eq $env:COMPUTERNAME -or $ComputerName -eq 'localhost' -or $ComputerName -eq '127.0.0.1')
# 远程模式下强制使用基础进程信息(无网络统计)
if (-not $isLocal) {
Write-Warning "远程计算机 $ComputerName 不支持获取网络连接信息,将仅显示进程列表(无网络统计)"
}
# ---------- 1. 获取所有进程(统一使用 Win32_Process) ----------
try {
if ($isLocal) {
$allProcesses = Get-CimInstance -ClassName Win32_Process -ErrorAction Stop
} else {
$allProcesses = Get-CimInstance -ComputerName $ComputerName -ClassName Win32_Process -ErrorAction Stop
}
Write-Verbose "获取到 $($allProcesses.Count) 个进程"
} catch {
Write-Error "无法获取进程列表: $_"
exit 1
}
# ---------- 2. 获取服务映射(PID -> 服务信息) ----------
$serviceMap = @{}
if ($isLocal) {
try {
$services = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
$procId = $svc.ProcessId
if ($procId -and $procId -ne 0) {
if (-not $serviceMap.ContainsKey($procId)) {
$serviceMap[$procId] = @()
}
$serviceMap[$procId] += [PSCustomObject]@{
DisplayName = $svc.DisplayName
Name = $svc.Name
AcceptStop = $svc.AcceptStop
StartMode = $svc.StartMode
}
}
}
} catch {
Write-Warning "无法获取服务信息: $_"
}
}
# ---------- 3. 获取自启动项列表(仅本地) ----------
$autoStartPaths = @()
if ($isLocal) {
$regPaths = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($regPath in $regPaths) {
if (Test-Path $regPath) {
$items = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
foreach ($prop in $items.PSObject.Properties) {
if ($prop.Name -notin @('PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider')) {
$value = $prop.Value
if ($value -match '^"?([^"]+\.exe)"?') {
$exePath = $matches[1]
$autoStartPaths += $exePath
} elseif ($value -match '^(\S+\.exe)') {
$autoStartPaths += $matches[1]
}
}
}
}
}
$startupFolders = @(
[Environment]::GetFolderPath('Startup'),
"$env:ALLUSERSPROFILE\Microsoft\Windows\Start Menu\Programs\Startup"
)
foreach ($folder in $startupFolders) {
if (Test-Path $folder) {
$lnkFiles = Get-ChildItem -Path $folder -Filter *.lnk -ErrorAction SilentlyContinue
foreach ($lnk in $lnkFiles) {
$shell = New-Object -ComObject WScript.Shell
try {
$shortcut = $shell.CreateShortcut($lnk.FullName)
$targetPath = $shortcut.TargetPath
if ($targetPath -and (Test-Path $targetPath)) {
$autoStartPaths += $targetPath
}
} catch {
# 忽略无法解析的快捷方式
} finally {
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
}
}
}
}
$autoStartPaths = $autoStartPaths | Select-Object -Unique
}
# ---------- 4. 获取网络统计(仅本地) ----------
$tcpConnections = $null
$udpEndpoints = $null
if ($isLocal) {
try {
$tcpConnections = Get-NetTCPConnection -ErrorAction Stop
Write-Verbose "获取到 $($tcpConnections.Count) 条 TCP 连接"
} catch {
Write-Warning "无法获取 TCP 连接信息: $_"
$tcpConnections = @()
}
try {
$udpEndpoints = Get-NetUDPEndpoint -ErrorAction Stop
Write-Verbose "获取到 $($udpEndpoints.Count) 个 UDP 端点"
} catch {
Write-Warning "无法获取 UDP 端点信息: $_"
$udpEndpoints = @()
}
}
# 构建每个进程的网络统计映射和远程地址列表
$netStatsMap = @{} # key: PID, value: @{ TcpConnections, UdpEndpoints, RemoteAddresses = [] }
if ($isLocal) {
# 初始化所有 PID 的条目(后续合并)
if ($tcpConnections) {
foreach ($conn in $tcpConnections) {
$procId = $conn.OwningProcess
if ($procId -and $procId -ne 0) {
if (-not $netStatsMap.ContainsKey($procId)) {
$netStatsMap[$procId] = @{
TcpConnections = 0
UdpEndpoints = 0
RemoteAddresses = @()
}
}
$netStatsMap[$procId].TcpConnections++
# 收集远程地址(如果存在且不为空)
$remote = $conn.RemoteAddress
if ($remote -and $remote -ne '0.0.0.0' -and $remote -ne '::' -and $remote -ne '') {
$remotePort = $conn.RemotePort
$remoteEndpoint = if ($remotePort) { "$remote`:$remotePort" } else { $remote }
$netStatsMap[$procId].RemoteAddresses += $remoteEndpoint
}
}
}
}
if ($udpEndpoints) {
foreach ($endpoint in $udpEndpoints) {
$procId = $endpoint.OwningProcess
if ($procId -and $procId -ne 0) {
if (-not $netStatsMap.ContainsKey($procId)) {
$netStatsMap[$procId] = @{
TcpConnections = 0
UdpEndpoints = 0
RemoteAddresses = @()
}
}
$netStatsMap[$procId].UdpEndpoints++
# UDP 端点通常没有远程地址,不收集
}
}
}
}
# DNS 反向解析缓存
$dnsCache = @{}
function Resolve-IPToHost {
param([string]$IPAddress)
if ($dnsCache.ContainsKey($IPAddress)) {
return $dnsCache[$IPAddress]
}
try {
$hostEntry = [System.Net.Dns]::GetHostEntry($IPAddress)
$hostName = $hostEntry.HostName
# 如果解析出的域名就是IP本身,则不显示域名
if ($hostName -ne $IPAddress) {
$dnsCache[$IPAddress] = $hostName
return $hostName
} else {
$dnsCache[$IPAddress] = $null
return $null
}
} catch {
$dnsCache[$IPAddress] = $null
return $null
}
}
# 对于每个进程,统计远程地址 TOP10(按出现次数),并尝试解析域名
$remoteTop10Map = @{} # key: PID, value: 字符串数组,每行一个 "域名(IP):端口 (次数)" 或 "IP:端口 (次数)"
foreach ($procId in $netStatsMap.Keys) {
$addrList = $netStatsMap[$procId].RemoteAddresses
if ($addrList.Count -gt 0) {
# 分组统计(按原始 IP:端口)
$grouped = $addrList | Group-Object -NoElement | Sort-Object -Property Count -Descending | Select-Object -First 10
$top10Lines = @()
foreach ($grp in $grouped) {
$endpoint = $grp.Name # 格式 "IP:端口"
$count = $grp.Count
# 提取 IP 部分(去掉端口)
if ($endpoint -match '^(.*?):(\d+)$') {
$ip = $matches[1]
$port = $matches[2]
$domain = Resolve-IPToHost -IPAddress $ip
if ($domain) {
$displayEndpoint = "$domain($ip):$port"
} else {
$displayEndpoint = "$ip`:$port"
}
} else {
# 如果没有端口(理论上不会发生),直接使用原字符串
$displayEndpoint = $endpoint
}
$top10Lines += "$displayEndpoint ($count)"
}
$remoteTop10Map[$procId] = $top10Lines -join "; "
} else {
$remoteTop10Map[$procId] = ""
}
}
# ---------- 5. 构建进程映射表 ----------
$processMap = @{}
foreach ($p in $allProcesses) {
$procId = $p.ProcessId
if ($procId -eq $null -or $procId -eq 0) { continue }
# 可执行路径
$exePath = $p.ExecutablePath
if ([string]::IsNullOrWhiteSpace($exePath)) { $exePath = "(不可用)" }
# 关联服务
$servicesDisplay = ""
$servicesNames = @()
$canStopService = $false
if ($serviceMap.ContainsKey($procId)) {
$svcList = $serviceMap[$procId]
$displayNames = $svcList | ForEach-Object { $_.DisplayName }
$servicesDisplay = $displayNames -join "; "
$servicesNames = $svcList | ForEach-Object { $_.Name }
$canStopService = ($svcList | Where-Object { $_.AcceptStop -eq $true }).Count -gt 0
}
if ([string]::IsNullOrWhiteSpace($servicesDisplay)) { $servicesDisplay = "(无)" }
# 自启动判断
$isAuto = $false
if ($isLocal -and $exePath -ne "(不可用)" -and $autoStartPaths -contains $exePath) {
$isAuto = $true
}
# 网络统计
$tcpCount = 0
$udpCount = 0
$remoteTop10 = ""
if ($netStatsMap.ContainsKey($procId)) {
$tcpCount = $netStatsMap[$procId].TcpConnections
$udpCount = $netStatsMap[$procId].UdpEndpoints
$remoteTop10 = if ($remoteTop10Map.ContainsKey($procId)) { $remoteTop10Map[$procId] } else { "" }
}
$totalEndpoints = $tcpCount + $udpCount
# 可关闭性判断(与内存脚本逻辑一致)
$procName = $p.Name -replace '\.exe$', ''
$criticalProcesses = @("System", "Idle", "csrss", "wininit", "winlogon", "services", "lsass", "smss", "svchost")
$isKillable = $false
$closeMethod = ""
if ($servicesNames.Count -gt 0) {
if ($canStopService) {
$isKillable = $true
$closeMethod = "可停止服务: 运行 'sc config <服务名> start= disabled' 禁用,然后 'net stop <服务名>' 停止"
} else {
$isKillable = $false
$closeMethod = "服务不可停止或为系统关键服务,不建议关闭"
}
} else {
if ($criticalProcesses -notcontains $procName -and $p.SessionId -ne 0) {
$isKillable = $true
$closeMethod = "用户进程,可在任务管理器中结束任务"
} else {
$isKillable = $false
$closeMethod = "系统关键进程或后台服务,不建议关闭"
}
}
if ($isAuto) {
$closeMethod += " (自启动项: 使用 msconfig 或任务管理器-启动选项卡禁用)"
}
# 存储
$processMap[$procId] = [PSCustomObject]@{
Name = $procName
ParentPid = $p.ParentProcessId
TcpConnections = $tcpCount
UdpEndpoints = $udpCount
TotalEndpoints = $totalEndpoints
RemoteTop10 = $remoteTop10
ExecutablePath = $exePath
Services = $servicesDisplay
IsAutoStart = $isAuto
IsKillable = $isKillable
CloseMethod = $closeMethod.TrimStart(';').Trim()
SortValue = $null
}
}
# 补充自启动判断:服务自动启动的影响
if ($isLocal) {
$serviceStartMode = @{}
try {
$servicesAll = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $servicesAll) {
$serviceStartMode[$svc.Name] = $svc.StartMode
}
} catch { }
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
if (-not $proc.IsAutoStart -and $serviceMap.ContainsKey($procId)) {
foreach ($svcInfo in $serviceMap[$procId]) {
if ($serviceStartMode.ContainsKey($svcInfo.Name) -and $serviceStartMode[$svcInfo.Name] -eq "Auto") {
$proc.IsAutoStart = $true
if ([string]::IsNullOrWhiteSpace($proc.CloseMethod)) {
$proc.CloseMethod = "服务自动启动: 使用 services.msc 将启动类型改为'手动'或'禁用'"
} else {
$proc.CloseMethod += "; 服务自动启动: 可改为手动或禁用"
}
break
}
}
}
}
}
# 转换 IsAutoStart, IsKillable 为字符串
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
$proc.IsAutoStart = if ($proc.IsAutoStart) { "是" } else { "否" }
$proc.IsKillable = if ($proc.IsKillable) { "是" } else { "否" }
}
# 确定排序字段的值
$sortProperty = $SortBy
foreach ($procId in $processMap.Keys) {
$proc = $processMap[$procId]
$proc.SortValue = $proc.$sortProperty
}
# ---------- 6. 构建子进程映射 ----------
$childrenMap = @{}
foreach ($procId in $processMap.Keys) {
$parentPid = $processMap[$procId].ParentPid
if ($parentPid -and $parentPid -ne 0 -and $processMap.ContainsKey($parentPid)) {
if (-not $childrenMap.ContainsKey($parentPid)) {
$childrenMap[$parentPid] = @()
}
$childrenMap[$parentPid] += $procId
}
}
# ---------- 7. 获取网络活动最高的 Top N 个父进程 ----------
$sortedParents = $processMap.Keys | ForEach-Object {
$proc = $processMap[$_]
[PSCustomObject]@{
PID = $_
Name = $proc.Name
SortValue = $proc.SortValue
TcpConnections = $proc.TcpConnections
UdpEndpoints = $proc.UdpEndpoints
TotalEndpoints = $proc.TotalEndpoints
RemoteTop10 = $proc.RemoteTop10
ExecutablePath = $proc.ExecutablePath
Services = $proc.Services
IsAutoStart = $proc.IsAutoStart
IsKillable = $proc.IsKillable
CloseMethod = $proc.CloseMethod
}
} | Sort-Object -Property SortValue -Descending | Select-Object -First $Top
# ---------- 8. 为每个父进程收集子进程详情 ----------
$reportData = @()
foreach ($parent in $sortedParents) {
$parentPid = $parent.PID
$childPids = if ($childrenMap.ContainsKey($parentPid)) { $childrenMap[$parentPid] } else { @() }
$childList = @()
$childTotalTcp = 0
$childTotalUdp = 0
$childTotalTotal = 0
foreach ($childPid in $childPids) {
$child = $processMap[$childPid]
$childTotalTcp += $child.TcpConnections
$childTotalUdp += $child.UdpEndpoints
$childTotalTotal += $child.TotalEndpoints
$childList += [PSCustomObject]@{
ChildName = $child.Name
ChildPID = $childPid
ChildTcpConnections = $child.TcpConnections
ChildUdpEndpoints = $child.UdpEndpoints
ChildTotalEndpoints = $child.TotalEndpoints
ChildRemoteTop10 = $child.RemoteTop10
ChildExecutablePath = $child.ExecutablePath
ChildServices = $child.Services
ChildIsAutoStart = $child.IsAutoStart
ChildIsKillable = $child.IsKillable
ChildCloseMethod = $child.CloseMethod
}
}
$reportData += [PSCustomObject]@{
ParentPID = $parentPid
ParentName = $parent.Name
ParentTcpConnections = $parent.TcpConnections
ParentUdpEndpoints = $parent.UdpEndpoints
ParentTotalEndpoints = $parent.TotalEndpoints
ParentRemoteTop10 = $parent.RemoteTop10
ParentExecutablePath = $parent.ExecutablePath
ParentServices = $parent.Services
ParentIsAutoStart = $parent.IsAutoStart
ParentIsKillable = $parent.IsKillable
ParentCloseMethod = $parent.CloseMethod
ChildCount = $childPids.Count
ChildTotalTcp = $childTotalTcp
ChildTotalUdp = $childTotalUdp
ChildTotalTotal = $childTotalTotal
Children = $childList
}
}
# ---------- 9. 控制台输出 ----------
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportTitle = "进程网络分析报告 - $ComputerName ($timestamp)"
$sortDesc = switch ($SortBy) {
'TcpConnections' { "按 TCP 连接数降序,显示 Top $Top" }
'UdpEndpoints' { "按 UDP 端点数降序,显示 Top $Top" }
'TotalEndpoints' { "按总端点数(TCP+UDP)降序,显示 Top $Top" }
}
Write-Host "`n========== $reportTitle ==========" -ForegroundColor Cyan
Write-Host "排序方式: $sortDesc" -ForegroundColor Yellow
Write-Host "`n父进程列表 (网络活动最高的 $Top 个):`n" -ForegroundColor Green
foreach ($item in $reportData) {
Write-Host ">>> 父进程: $($item.ParentName) (PID: $($item.ParentPID))" -ForegroundColor Magenta
Write-Host " TCP 连接数: $($item.ParentTcpConnections)" -ForegroundColor Gray
Write-Host " UDP 端点数: $($item.ParentUdpEndpoints)" -ForegroundColor Gray
Write-Host " 总端点数: $($item.ParentTotalEndpoints)" -ForegroundColor Gray
if ($item.ParentRemoteTop10) {
Write-Host " 远程地址 TOP10: $($item.ParentRemoteTop10)" -ForegroundColor DarkCyan
}
Write-Host " 路径: $($item.ParentExecutablePath)" -ForegroundColor Gray
Write-Host " 关联服务: $($item.ParentServices)" -ForegroundColor Gray
Write-Host " 自启动: $($item.ParentIsAutoStart)" -ForegroundColor Gray
Write-Host " 是否可关闭: $($item.ParentIsKillable)" -ForegroundColor Gray
Write-Host " 关闭/禁用建议: $($item.ParentCloseMethod)" -ForegroundColor Gray
Write-Host " 子进程数量: $($item.ChildCount)" -ForegroundColor Gray
Write-Host " 子进程总 TCP: $($item.ChildTotalTcp), 总 UDP: $($item.ChildTotalUdp), 总端点: $($item.ChildTotalTotal)" -ForegroundColor Gray
if ($item.ChildCount -gt 0) {
Write-Host " 子进程详情:" -ForegroundColor Gray
foreach ($child in $item.Children) {
Write-Host " - $($child.ChildName) (PID:$($child.ChildPID)) - TCP:$($child.ChildTcpConnections) UDP:$($child.ChildUdpEndpoints) 总:$($child.ChildTotalEndpoints)" -ForegroundColor DarkGray
if ($child.ChildRemoteTop10) {
Write-Host " 远程地址 TOP10: $($child.ChildRemoteTop10)" -ForegroundColor DarkCyan
}
Write-Host " 路径: $($child.ChildExecutablePath)" -ForegroundColor DarkGray
Write-Host " 服务: $($child.ChildServices)" -ForegroundColor DarkGray
Write-Host " 自启动: $($child.ChildIsAutoStart)" -ForegroundColor DarkGray
Write-Host " 可关闭: $($child.ChildIsKillable)" -ForegroundColor DarkGray
Write-Host " 建议: $($child.ChildCloseMethod)" -ForegroundColor DarkGray
}
} else {
Write-Host " 无子进程" -ForegroundColor DarkGray
}
Write-Host ""
}
# ---------- 10. 导出报告(CSV / HTML) ----------
if ($OutputPath) {
$directory = Split-Path $OutputPath -Parent
if ($directory -and -not (Test-Path $directory)) {
New-Item -Path $directory -ItemType Directory -Force | Out-Null
}
$extension = [System.IO.Path]::GetExtension($OutputPath).ToLower()
try {
if ($extension -eq '.csv') {
$csvRows = @()
foreach ($item in $reportData) {
if ($item.Children.Count -eq 0) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程TCP连接数 = $item.ParentTcpConnections
父进程UDP端点数 = $item.ParentUdpEndpoints
父进程总端点 = $item.ParentTotalEndpoints
父进程远程地址TOP10 = $item.ParentRemoteTop10
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $null
子进程名称 = $null
子进程TCP连接数 = $null
子进程UDP端点数 = $null
子进程总端点 = $null
子进程远程地址TOP10 = $null
子进程路径 = $null
子进程服务 = $null
子进程自启动 = $null
子进程可关闭 = $null
子进程建议 = $null
}
} else {
foreach ($child in $item.Children) {
$csvRows += [PSCustomObject]@{
父进程PID = $item.ParentPID
父进程名称 = $item.ParentName
父进程TCP连接数 = $item.ParentTcpConnections
父进程UDP端点数 = $item.ParentUdpEndpoints
父进程总端点 = $item.ParentTotalEndpoints
父进程远程地址TOP10 = $item.ParentRemoteTop10
父进程路径 = $item.ParentExecutablePath
父进程服务 = $item.ParentServices
父进程自启动 = $item.ParentIsAutoStart
父进程可关闭 = $item.ParentIsKillable
父进程建议 = $item.ParentCloseMethod
子进程PID = $child.ChildPID
子进程名称 = $child.ChildName
子进程TCP连接数 = $child.ChildTcpConnections
子进程UDP端点数 = $child.ChildUdpEndpoints
子进程总端点 = $child.ChildTotalEndpoints
子进程远程地址TOP10 = $child.ChildRemoteTop10
子进程路径 = $child.ChildExecutablePath
子进程服务 = $child.ChildServices
子进程自启动 = $child.ChildIsAutoStart
子进程可关闭 = $child.ChildIsKillable
子进程建议 = $child.ChildCloseMethod
}
}
}
}
$csvRows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "`n报告已导出为 CSV: $OutputPath" -ForegroundColor Green
} elseif ($extension -eq '.html') {
$html = @"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>$reportTitle</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #2c3e50; }
h2 { color: #34495e; border-bottom: 1px solid #ccc; }
.parent-table { border-collapse: collapse; width: 100%; margin-bottom: 30px; }
.parent-table th, .parent-table td { border: 1px solid #ddd; padding: 8px; text-align: left; vertical-align: top; }
.parent-table th { background-color: #4CAF50; color: white; }
.child-table { width: 100%; border-collapse: collapse; margin-top: 5px; }
.child-table th, .child-table td { border: 1px solid #ccc; padding: 4px; font-size: 0.9em; }
.child-table th { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>$reportTitle</h1>
<div class='summary'>
<p><strong>排序依据:</strong> $sortDesc</p>
</div>
"@
foreach ($item in $reportData) {
$html += "<h2>父进程: $($item.ParentName) (PID $($item.ParentPID))</h2>"
$html += "<table class='parent-table'>"
$html += "<tr><th style='width:150px'>属性</th><th>值</th></tr>"
$html += "<tr>。<TCP 连接数</th><td>$($item.ParentTcpConnections)</td></tr>"
$html += "<tr>。<UDP 端点数</th><td>$($item.ParentUdpEndpoints)</td></tr>"
$html += "<tr>。<总端点数</th><td>$($item.ParentTotalEndpoints)</td></tr>"
$html += "<tr><th>远程地址 TOP10</th><td>$($item.ParentRemoteTop10 -replace '; ', '<br>')</td></tr>"
$html += "<tr>。<路径</th><td>$($item.ParentExecutablePath)</td></tr>"
$html += "<tr>。<关联服务</th><td>$($item.ParentServices)</td></tr>"
$html += "<tr>。<自启动</th><td>$($item.ParentIsAutoStart)</td></tr>"
$html += "<tr>。<可关闭</th><td>$($item.ParentIsKillable)</td></tr>"
$html += "<tr>。<关闭/禁用建议</th><td>$($item.ParentCloseMethod)</td></tr>"
$html += "<tr>。<子进程数量</th><td>$($item.ChildCount)</td></tr>"
$html += "<tr>。<子进程总 TCP</th><td>$($item.ChildTotalTcp)</td></tr>"
$html += "<tr>。<子进程总 UDP</th><td>$($item.ChildTotalUdp)NUMPages?"
$html += "<tr>。<子进程总端点</th><td>$($item.ChildTotalTotal)NUMPages?"
$html += "</table>"
if ($item.Children.Count -gt 0) {
$html += "<h3>子进程列表</h3>"
$html += "<table class='child-table'>"
$html += "<tr><th>子进程名称</th><th>PID</th><th>TCP连接数</th><th>UDP端点数</th><th>总端点</th><th>远程地址TOP10</th><th>路径</th><th>关联服务</th><th>自启动</th><th>可关闭</th><th>建议</th></tr>"
foreach ($child in $item.Children) {
$html += "<tr>"
$html += "<td>$($child.ChildName)</td>"
$html += "<td>$($child.ChildPID)</td>"
$html += "<td>$($child.ChildTcpConnections)</td>"
$html += "<td>$($child.ChildUdpEndpoints)</td>"
$html += "<td>$($child.ChildTotalEndpoints)</td>"
$html += "<td>$($child.ChildRemoteTop10 -replace '; ', '<br>')</td>"
$html += "<td>$($child.ChildExecutablePath)</td>"
$html += "<td>$($child.ChildServices)</td>"
$html += "<td>$($child.ChildIsAutoStart)</td>"
$html += "<td>$($child.ChildIsKillable)</td>"
$html += "<td>$($child.ChildCloseMethod)</td>"
$html += "</tr>"
}
$html += "</table>"
} else {
$html += "<p><em>无子进程</em></p>"
}
}
$html += "</body></html>"
$html | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "`n报告已导出为 HTML: $OutputPath" -ForegroundColor Green
} else {
Write-Warning "不支持的文件扩展名 '$extension',仅支持 .csv 或 .html。"
}
} catch {
Write-Error "导出失败: $_"
}
}
Write-Host "`n分析完成。" -ForegroundColor Cyan
pause
4.IO工具:
说明:工具抓取时间略长,请耐心等待
4.实时I/O 监控分析工具
该 PowerShell 脚本实时监控本地进程的磁盘 I/O 读写速率(基于 Get-Counter 性能计数器),按读取、写入或总速率排序,默认显示占用最高的 20 个进程。
对每个高 I/O 进程,脚本会展示:
- 瞬时读写速率(KB/s)
- 主程序路径、启动时间
- 关联的 Windows 服务(显示名称,多个以逗号分隔)
- 智能清理建议:若进程托管了可停止的服务,提示停止服务的命令(
Stop-Service);否则根据是否为系统关键进程给出终止或保留建议。
支持导出 HTML 或 CSV 报告,自动请求管理员权限,适用于快速定位磁盘高负载进程及其服务依赖,辅助系统优化与故障排查。
代码:
点击查看代码
<#
.SYNOPSIS
实时监控进程磁盘 I/O 读写速率(Get-Counter 方式),显示占用最高的 Top N 进程及其详细信息
.DESCRIPTION
通过 Get-Counter 获取所有进程的 IO Read/Write Bytes/sec,仅对有 I/O 活动的进程获取详细信息,
按指定排序方式取前 N 个进程,展示格式与原磁盘空间报告一致。
.PARAMETER Top
显示磁盘 I/O 最高的 N 个进程,默认 20。
.PARAMETER SortBy
排序依据:ReadRate(读取速率)、WriteRate(写入速率)、TotalRate(总速率,默认)。
.PARAMETER OutputPath
导出报告的路径,支持 .csv 或 .html。不指定则仅输出到控制台。
.PARAMETER ComputerName
要监控的计算机名(必须为本地计算机)。
.PARAMETER RootPath
保留参数,已废弃。
.EXAMPLE
.\makeDiskRptV3.ps1
.EXAMPLE
.\makeDiskRptV3.ps1 -Top 10 -SortBy ReadRate -OutputPath io.html
#>
[CmdletBinding()]
param (
[ValidateRange(1, [int]::MaxValue)]
[int]$Top = 20,
[ValidateSet('ReadRate', 'WriteRate', 'TotalRate')]
[string]$SortBy = 'TotalRate',
[string]$OutputPath = '实时IO报告.html',
[string]$ComputerName = $env:COMPUTERNAME,
[string]$RootPath = $null
)
$OutputParent = Split-Path $MyInvocation.MyCommand.Path -Parent
$OutputPath = Join-Path $OutputParent '实时IO报告.html'
# 自动提权
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "请求管理员权限..." -ForegroundColor Yellow
$scriptPath = $MyInvocation.MyCommand.Path
$arguments = @()
foreach ($key in $PSBoundParameters.Keys) {
$value = $PSBoundParameters[$key]
if ($value -is [switch]) { if ($value) { $arguments += "-$key" } }
else { $arguments += "-$key", "`"$value`"" }
}
Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`" $($arguments -join ' ')" -Verb RunAs
exit
}
# 仅支持本地
if ($ComputerName -ne $env:COMPUTERNAME -and $ComputerName -notin @('localhost','127.0.0.1')) {
Write-Error "实时 I/O 监控仅支持本地计算机。"
exit 1
}
if ($RootPath) { Write-Warning "参数 -RootPath 已废弃" }
# ---------- 辅助函数 ----------
function Test-ProcessCleanable {
param([System.Diagnostics.Process]$Process)
$critical = @('Idle','System','Secure System','Registry','csrss','wininit','services','lsass','winlogon','svchost','fontdrvhost','dwm','explorer')
return $critical -notcontains $Process.ProcessName
}
function Get-CleanMethod {
param([System.Diagnostics.Process]$Process)
if (Test-ProcessCleanable -Process $Process) {
return "可安全终止(Stop-Process -Id $($Process.Id))或使用任务管理器结束进程。"
} else {
return "系统关键进程,不建议终止。"
}
}
# 获取进程关联的服务(显示名称)
function Get-ProcessServices {
param([int]$ProcessId)
try {
$services = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessId -eq $ProcessId -and $_.Name -ne '' }
if ($services) {
$displayNames = $services | Select-Object -ExpandProperty DisplayName -Unique
return $displayNames -join ', '
}
} catch { }
return $null
}
# 根据服务生成清理建议
function Get-ServiceCleanMethod {
param([int]$ProcessId, [string]$ProcessName)
$services = Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessId -eq $ProcessId -and $_.Name -ne '' }
if (-not $services) { return $null }
$stopableServices = $services | Where-Object { $_.State -eq 'Running' -and $_.StartMode -ne 'Disabled' }
if ($stopableServices) {
$serviceNames = $stopableServices | Select-Object -ExpandProperty DisplayName -Unique
$nameList = $serviceNames -join '", "'
return "该进程托管以下可停止的服务:`"$nameList`"。可尝试停止服务(Stop-Service -DisplayName `"服务显示名称`"),停止服务后进程可能自动结束。"
} else {
$serviceNames = $services | Select-Object -ExpandProperty DisplayName -Unique
$nameList = $serviceNames -join '", "'
return "该进程托管服务(`"$nameList`"),但服务未运行或不可停止,不建议直接终止进程。"
}
}
# ---------- 1. 从 Get-Counter 获取读写速率 ----------
Write-Host "正在获取进程磁盘 I/O 速率(Get-Counter 一次采样)..." -ForegroundColor Cyan
$readCounter = Get-Counter "\Process(*)\IO Read Bytes/sec" -ErrorAction SilentlyContinue
$writeCounter = Get-Counter "\Process(*)\IO Write Bytes/sec" -ErrorAction SilentlyContinue
if (-not $readCounter -or -not $writeCounter) {
Write-Error "无法获取性能计数器数据,请确保以管理员身份运行。"
exit 1
}
# 过滤无效实例,解析实例名和速率
$readSamples = $readCounter.CounterSamples | Where-Object { $_.InstanceName -notin @('_total','idle') -and $_.InstanceName }
$writeSamples = $writeCounter.CounterSamples | Where-Object { $_.InstanceName -notin @('_total','idle') -and $_.InstanceName }
# 构建 实例名 -> 速率 的映射
$readMap = @{}
$readSamples | ForEach-Object { $readMap[$_.InstanceName] = $_.CookedValue }
$writeMap = @{}
$writeSamples | ForEach-Object { $writeMap[$_.InstanceName] = $_.CookedValue }
# 收集所有出现过的实例名(去重)
$allInstances = ($readMap.Keys + $writeMap.Keys) | Select-Object -Unique
# 按基础进程名分组实例(基础名 = 去掉 #数字 后缀)
$instanceGroups = @{}
foreach ($inst in $allInstances) {
$baseName = $inst -replace '#\d+$', ''
$index = if ($inst -match '#(\d+)$') { [int]$matches[1] } else { 0 }
if (-not $instanceGroups.ContainsKey($baseName)) { $instanceGroups[$baseName] = @() }
$instanceGroups[$baseName] += [PSCustomObject]@{ FullName = $inst; Index = $index }
}
# 对每组按 Index 排序
$tmpGroups = [System.Management.Automation.PSSerializer]::Deserialize([System.Management.Automation.PSSerializer]::Serialize($instanceGroups))
foreach ($baseName in $tmpGroups) {
$instanceGroups[$baseName] = $tmpGroups[$baseName] | Sort-Object Index
}
# ---------- 2. 按需获取进程信息(仅对出现过的进程名)----------
$processInfoMap = @{} # key: 实例全名, value: 进程对象及速率
foreach ($baseName in $instanceGroups.Keys) {
# 获取该进程名的所有当前进程(按 PID 排序)
$procs = Get-Process -Name $baseName -ErrorAction SilentlyContinue | Sort-Object Id
$instances = $instanceGroups[$baseName]
# 按顺序匹配:第 i 个进程对应第 i 个实例(索引排序)
for ($i = 0; $i -lt [Math]::Min($procs.Count, $instances.Count); $i++) {
$proc = $procs[$i]
$instFullName = $instances[$i].FullName
$readRateKBps = if ($readMap.ContainsKey($instFullName) -and $readMap[$instFullName] -ge 0) {
[math]::Round($readMap[$instFullName] / 1KB, 2)
} else { 0 }
$writeRateKBps = if ($writeMap.ContainsKey($instFullName) -and $writeMap[$instFullName] -ge 0) {
[math]::Round($writeMap[$instFullName] / 1KB, 2)
} else { 0 }
$totalRateKBps = $readRateKBps + $writeRateKBps
# 获取进程关联的服务
$services = Get-ProcessServices -ProcessId $proc.Id
$serviceCleanMethod = Get-ServiceCleanMethod -ProcessId $proc.Id -ProcessName $proc.ProcessName
# 其他属性(仅保留路径和启动时间)
try {
$path = $proc.MainModule.FileName
$startTime = $proc.StartTime
} catch {
$path = "无法获取"; $startTime = $null
}
$processInfoMap[$instFullName] = [PSCustomObject]@{
InstanceName = $instFullName
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ReadRateKBps = $readRateKBps
WriteRateKBps = $writeRateKBps
TotalRateKBps = $totalRateKBps
ExecutablePath = $path
StartTime = $startTime
AssociatedServices = $services
ServiceCleanMethod = $serviceCleanMethod
IsCleanable = Test-ProcessCleanable -Process $proc
CleanMethod = Get-CleanMethod -Process $proc
}
}
}
# 转换为列表并按指定排序
$processList = $processInfoMap.Values
switch ($SortBy) {
'ReadRate' { $processList = $processList | Sort-Object ReadRateKBps -Descending }
'WriteRate' { $processList = $processList | Sort-Object WriteRateKBps -Descending }
'TotalRate' { $processList = $processList | Sort-Object TotalRateKBps -Descending }
}
$topProcesses = $processList | Select-Object -First $Top
# ---------- 3. 构建报告数据(父进程+子项)----------
$reportData = @()
foreach ($proc in $topProcesses) {
$children = @(
[PSCustomObject]@{ Metric = "读取速率 (KB/s)"; Value = $proc.ReadRateKBps; Unit = "KB/s" }
[PSCustomObject]@{ Metric = "写入速率 (KB/s)"; Value = $proc.WriteRateKBps; Unit = "KB/s" }
[PSCustomObject]@{ Metric = "总 I/O 速率 (KB/s)"; Value = $proc.TotalRateKBps; Unit = "KB/s" }
[PSCustomObject]@{ Metric = "主程序路径"; Value = $proc.ExecutablePath; Unit = "" }
[PSCustomObject]@{ Metric = "启动时间"; Value = $proc.StartTime; Unit = "" }
)
# 添加关联服务子项(如果有)
if ($proc.AssociatedServices) {
$children += [PSCustomObject]@{ Metric = "关联服务(显示名称)"; Value = $proc.AssociatedServices; Unit = "" }
} else {
$children += [PSCustomObject]@{ Metric = "关联服务"; Value = "无"; Unit = "" }
}
# 确定最终清理建议:优先使用服务相关建议,否则用原进程建议
$finalCleanMethod = if ($proc.ServiceCleanMethod) { $proc.ServiceCleanMethod } else { $proc.CleanMethod }
$reportData += [PSCustomObject]@{
ParentProcessName = "$($proc.ProcessName) (PID: $($proc.ProcessId))"
ReadRateKBps = $proc.ReadRateKBps
WriteRateKBps = $proc.WriteRateKBps
TotalRateKBps = $proc.TotalRateKBps
IsCleanable = $proc.IsCleanable
CleanMethod = $finalCleanMethod
Children = $children
}
}
# ---------- 4. 控制台输出 ----------
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportTitle = "实时磁盘 I/O 监控报告 (Get-Counter) - $ComputerName ($timestamp)"
$sortDesc = switch ($SortBy) {
'ReadRate' { "按读取速率降序,Top $Top" }
'WriteRate' { "按写入速率降序,Top $Top" }
'TotalRate' { "按总速率降序,Top $Top" }
}
Write-Host "`n========== $reportTitle ==========" -ForegroundColor Cyan
Write-Host "采样方式: Get-Counter 瞬时速率" -ForegroundColor Yellow
Write-Host "排序方式: $sortDesc" -ForegroundColor Yellow
Write-Host "`n高 I/O 进程列表 (Top $Top):`n" -ForegroundColor Green
foreach ($item in $reportData) {
Write-Host ">>> 进程: $($item.ParentProcessName)" -ForegroundColor Magenta
Write-Host " 读取速率: $($item.ReadRateKBps) KB/s" -ForegroundColor Gray
Write-Host " 写入速率: $($item.WriteRateKBps) KB/s" -ForegroundColor Gray
Write-Host " 总速率: $($item.TotalRateKBps) KB/s" -ForegroundColor Gray
Write-Host " 是否可清理: $(if ($item.IsCleanable) { '是' } else { '否' })" -ForegroundColor Gray
Write-Host " 清理建议: $($item.CleanMethod)" -ForegroundColor Gray
Write-Host " 详细指标:" -ForegroundColor Gray
foreach ($child in $item.Children) {
Write-Host " - $($child.Metric): $($child.Value) $($child.Unit)" -ForegroundColor DarkGray
}
Write-Host ""
}
# ---------- 5. 导出 CSV/HTML ----------
if ($OutputPath) {
$dir = Split-Path $OutputPath -Parent
if ($dir -and -not (Test-Path $dir)) { New-Item -Path $dir -ItemType Directory -Force | Out-Null }
$ext = [System.IO.Path]::GetExtension($OutputPath).ToLower()
try {
if ($ext -eq '.csv') {
$csvRows = @()
foreach ($item in $reportData) {
if ($item.Children.Count -eq 0) {
$csvRows += [PSCustomObject]@{
进程名_PID = $item.ParentProcessName
读取速率_KBps = $item.ReadRateKBps
写入速率_KBps = $item.WriteRateKBps
总速率_KBps = $item.TotalRateKBps
可清理 = $(if ($item.IsCleanable) { '是' } else { '否' })
清理建议 = $item.CleanMethod
指标名称 = $null
指标值 = $null
}
} else {
foreach ($child in $item.Children) {
$csvRows += [PSCustomObject]@{
进程名_PID = $item.ParentProcessName
读取速率_KBps = $item.ReadRateKBps
写入速率_KBps = $item.WriteRateKBps
总速率_KBps = $item.TotalRateKBps
可清理 = $(if ($item.IsCleanable) { '是' } else { '否' })
清理建议 = $item.CleanMethod
指标名称 = $child.Metric
指标值 = "$($child.Value) $($child.Unit)"
}
}
}
}
$csvRows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "`n报告已导出为 CSV: $OutputPath" -ForegroundColor Green
} elseif ($ext -eq '.html') {
$html = @"
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>$reportTitle</title>
<style>
body { font-family: Arial; margin:20px; }
h1 { color:#2c3e50; }
h2 { color:#34495e; border-bottom:1px solid #ccc; }
.summary { background:#eef; padding:10px; margin-bottom:20px; border-radius:5px; }
.parent-table { border-collapse:collapse; width:100%; margin-bottom:30px; }
.parent-table th, .parent-table td { border:1px solid #ddd; padding:8px; text-align:left; vertical-align:top; }
.parent-table th { background:#4CAF50; color:white; }
.child-table { width:100%; border-collapse:collapse; margin-top:5px; }
.child-table th, .child-table td { border:1px solid #ccc; padding:4px; font-size:0.9em; }
.child-table th { background:#f0f0f0; }
</style>
</head>
<body>
<h1>$reportTitle</h1>
<div class='summary'>
<p><strong>采样方式:</strong> Get-Counter 瞬时速率</p>
<p><strong>排序依据:</strong> $sortDesc</p>
</div>
"@
foreach ($item in $reportData) {
$html += "<h2>进程: $($item.ParentProcessName)</h2>"
$html += "<table class='parent-table'>"
$html += "<tr><th>属性</th><th>值</th></tr>"
$html += "<tr><th>读取速率 (KB/s)</th><td>$($item.ReadRateKBps)</th></tr>"
$html += "<tr><th>写入速率 (KB/s)</th><td>$($item.WriteRateKBps)</th></tr>"
$html += "<tr><th>总 I/O 速率 (KB/s)</th><td>$($item.TotalRateKBps)</th></tr>"
$html += "<tr><th>是否可清理</th><td>$(if ($item.IsCleanable) { '是' } else { '否' })</th></tr>"
$html += "<tr><th>清理建议</th><td>$($item.CleanMethod)</th></tr>"
$html += "</table>"
if ($item.Children.Count -gt 0) {
$html += "<h3>详细指标</h3><table class='child-table'><tr><th>指标名称</th><th>指标值</th></tr>"
foreach ($child in $item.Children) {
$html += "<tr><th>$($child.Metric)</th><td>$($child.Value) $($child.Unit)</th></tr>"
}
$html += "</table>"
}
}
$html += "</body></html>"
$html | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "`n报告已导出为 HTML: $OutputPath" -ForegroundColor Green
} else {
Write-Warning "不支持的文件扩展名 '$ext',仅支持 .csv 或 .html。"
}
} catch { Write-Error "导出失败: $_" }
}
Write-Host "`n监控完成。" -ForegroundColor Cyan
pause

浙公网安备 33010602011771号