AI工具:个人电脑性能分析“四大名捕”,解决电脑慢上网慢问题

AI模型:Deepseek

开发方法:AI+人工

测试环境:windows11

保存:记事本ansi编码,所有类型,名称.ps1

运行:右键使用powershell运行

报权限错误方法:管理员运行powershell→Set-ExecutionPolicy RemoteSigned -Scope CurrentUser→y

代码:

1.内存分析工具:

说明:

  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.处理器分析工具:

说明:

  1. 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.网络分析工具:

说明:

  1. 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
posted @ 2026-10-28 13:26  humors221  阅读(3)  评论(0)    收藏  举报