LOL 更新后偶尔在进入游戏时会自动跳回桌面。搜索后发现有人提到可以用监控软件查看是哪个应用进程抢占了窗口焦点。受此启发我编写了一个 PowerShell 脚本通过实时监控前台窗口切换来揪出元凶。使用方法1. 下载与准备将WindowMonitor.ps1和启动监视器.bat两个文件放在同一目录下例如桌面或任意文件夹。2. 启动监视器方式一推荐双击启动监视器.bat文件脚本会自动启动并打开 PowerShell 窗口。方式二右键WindowMonitor.ps1→ 选择「使用 PowerShell 运行」。3. 观察与记录启动后控制台窗口会实时显示每次窗口焦点切换的信息格式如下[21:30:15.123] FOCUS [LOL客户端] (LeagueClientUx / PID:12345) [21:30:18.456] FOCUS [Google Chrome] (chrome / PID:67890) [21:30:18.789] FOCUS BACK TO DESKTOP (explorer.exe)正常游戏时焦点应保持在游戏窗口。如果突然跳回桌面控制台会以红色显示BACK TO DESKTOP记录同时日志中会记录跳转前最后一个获得焦点的进程帮助定位元凶。4. 查看日志脚本会在同目录下自动生成日志文件命名格式为WindowMonitor_Log_20260613_213000.txt。日志记录了每次焦点切换的详细时间、窗口标题、进程名和 PID方便事后分析。5. 停止监视在 PowerShell 窗口中按Ctrl C即可停止监视控制台会显示本次共记录了多少次焦点切换事件。脚本内容WindowMonitor.ps1文件内容如下# 窗口活动监视器 v2.0 (便携版) 功能实时监控前台窗口切换记录每次焦点变化的时间、窗口标题和进程 用法双击 启动监视器.bat 或右键此文件用 PowerShell 运行 日志同目录下自动生成日志文件 #Add-Type using System; using System.Runtime.InteropServices; using System.Text; public class WinAPI { [DllImport(user32.dll)] public static extern IntPtr GetForegroundWindow(); [DllImport(user32.dll, CharSet CharSet.Auto, SetLastError true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport(user32.dll)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport(user32.dll)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport(user32.dll)] public static extern bool IsWindowVisible(IntPtr hWnd); } $PollIntervalMs 200$LogDirSplit-Path-Parent$MyInvocation.MyCommand.Path$tsGet-Date-FormatyyyyMMdd_HHmmss$LogFileJoin-Path$LogDirWindowMonitor_Log_$ts.txt$ShowDesktopAsFocus$truefunctionGet-ForegroundInfo{$hwnd[WinAPI]::GetForegroundWindow()if($hwnd-eq[IntPtr]::Zero){return$null}$len[WinAPI]::GetWindowTextLength($hwnd)$sbNew-ObjectSystem.Text.StringBuilder($len1)[WinAPI]::GetWindowText($hwnd,$sb,$sb.Capacity)|Out-Null$title$sb.ToString()$pidOut[uint32]0[WinAPI]::GetWindowThreadProcessId($hwnd,[ref]$pidOut)|Out-Null$procNametry{$procGet-Process-Id$pidOut-ErrorAction SilentlyContinueif($proc){$procName$proc.ProcessName}}catch{}return{Hwnd $hwndTitle $titleProcName $procNamePid $pidOut}}$Host.UI.RawUI.WindowTitle Window Monitor v2.0Write-HostWrite-Host -ForegroundColor CyanWrite-Host Window Monitor v2.0 (Portable)-ForegroundColor CyanWrite-Host -ForegroundColor CyanWrite-HostWrite-Host Log:$LogFile-ForegroundColor YellowWrite-Host Poll: ${PollIntervalMs}ms | CtrlC to stop-ForegroundColor YellowWrite-HostWrite-Host -------------------------------------------------ForegroundColor DarkGrayWrite-Host$headerWindow Monitor v2.0 LogrnStart:$(Get-Date-Formatyyyy-MM-dd HH:mm:ss)rnPoll: ${PollIntervalMs}msrnrn$header|Out-File-FilePath$LogFile-Encoding UTF8$lastHwnd[IntPtr]::Zero$eventCount 0try{while($true){$infoGet-ForegroundInfoif($null-ne$info-and$info.Hwnd-ne$lastHwnd){$nowGet-Date-FormatHH:mm:ss.fff$t$info.Title$p$info.ProcName$pidVal$info.Pidif([string]::IsNullOrEmpty($t)){$t(no title)}if([string]::IsNullOrEmpty($p)){$punknown}$isDesktop($p-eqexplorer-and($info.Title-eq-or$info.Title-eqProgram Manager))if($isDesktop-and-not$ShowDesktopAsFocus){Start-Sleep-Milliseconds$PollIntervalMscontinue}$eventCountif($isDesktop){$line [$now] BACK TO DESKTOP (explorer.exe)$colorRed}else{$line [$now] FOCUS [$t] ($p/ PID:$pidVal)$colorWhite}Write-Host$line-ForegroundColor$color$logLine[$now] FOCUS Title:$t| Process:$p| PID:$pidVal$logLine|Out-File-FilePath$LogFile-Encoding UTF8-Append$lastHwnd$info.Hwnd}Start-Sleep-Milliseconds$PollIntervalMs}}finally{Write-HostWrite-Host -------------------------------------------------ForegroundColor DarkGrayWrite-Host Stopped.$eventCountevents recorded.-ForegroundColor YellowWrite-Host Log:$LogFile-ForegroundColor YellowWrite-Host}WindowMonitor.ps1文件v2.1版本内容如下#Window Monitor v2.1(Portable - Hybrid Edition)- 50ms fast polling to catch quick popups - Auto snapshot of all visible windows when BACK TO DESKTOP detected - Monitor window minimized to avoid stealing game focus Usage: double-click the .bat launcher or run this .ps1inPowerShell#Add-Type using System; using System.Runtime.InteropServices; using System.Text; using System.Collections.Generic; public class WinAPI2 { [DllImport(user32.dll)] public static extern IntPtr GetForegroundWindow(); [DllImport(user32.dll, CharSet CharSet.Auto, SetLastError true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport(user32.dll)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport(user32.dll)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport(user32.dll)] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport(user32.dll)] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport(user32.dll)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport(user32.dll)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport(kernel32.dll)] public static extern IntPtr GetConsoleWindow(); public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; } public const int SW_MINIMIZE 6; public const int SW_SHOWMINNOACTIVE 7; public static void MinimizeConsole() { IntPtr console GetConsoleWindow(); if (console ! IntPtr.Zero) { ShowWindow(console, SW_MINIMIZE); } } public static Liststring GetVisibleWindowSnapshot() { var result new Liststring(); EnumWindows(delegate(IntPtr hWnd, IntPtr param) { if (IsWindowVisible(hWnd)) { int len GetWindowTextLength(hWnd); if (len 0) { StringBuilder sb new StringBuilder(len 1); GetWindowText(hWnd, sb, sb.Capacity); string title sb.ToString(); uint pid 0; GetWindowThreadProcessId(hWnd, out pid); string procName ;try{var pSystem.Diagnostics.Process.GetProcessById((int)pid);if(p!null)procNamep.ProcessName;} catch {} RECT rect;GetWindowRect(hWnd,out rect);int wrect.Right-rect.Left;int hrect.Bottom-rect.Top;if(w5h5procName!){ result.Add(string.Format({0}|{1}|PID:{2}|{3}x{4},title,procName,pid,w,h));}}}returntrue;}, IntPtr.Zero);returnresult;}}$PollIntervalMs 50$LogDir Split-Path -Parent$MyInvocation.MyCommand.Path$ts Get-Date -Format yyyyMMdd_HHmmss$LogFile Join-Path$LogDirWindowMonitor_Log_$ts.txt$Host.UI.RawUI.WindowTitle Window Monitor v2.1 Write-Host Write-Host -ForegroundColorCyan Write-Host Window Monitor v2.1 (Hybrid)-ForegroundColorCyan Write-Host -ForegroundColorCyan Write-HostWrite-Host Log:$LogFile-ForegroundColorYellow Write-Host Poll:${PollIntervalMs}ms desktop snapshot-ForegroundColorYellow Write-Host CtrlC to stop-ForegroundColorYellow Write-HostWrite-Host Monitor will minimize in 3 seconds...-ForegroundColorDarkGray Start-Sleep-Seconds3[WinAPI2]::MinimizeConsole()Write-HostWrite-Host -------------------------------------------------ForegroundColorDarkGray Write-Host$headerWindow Monitor v2.1 LogrnStart:$(Get-Date-Formatyyyy-MM-dd HH:mm:ss)rnPoll:${PollIntervalMs}ms snapshot on desktoprnrn$header|Out-File-FilePath$LogFile-EncodingUTF8$lastHwnd[IntPtr]::Zero$eventCount0$myPid$PIDtry{while($true){$hwnd[WinAPI2]::GetForegroundWindow()if($hwnd-ne[IntPtr]::Zero-and$hwnd-ne$lastHwnd){$nowGet-Date-FormatHH:mm:ss.fff$len[WinAPI2]::GetWindowTextLength($hwnd)$sbNew-Object System.Text.StringBuilder($len1)[WinAPI2]::GetWindowText($hwnd,$sb,$sb.Capacity)|Out-Null$rawTitle$sb.ToString()$pidOut[UInt32]0[WinAPI2]::GetWindowThreadProcessId($hwnd,[ref]$pidOut)|Out-Null$procNametry{$procGet-Process-Id$pidOut-ErrorActionSilentlyContinueif($proc){$procName$proc.ProcessName}}catch{}# Skip our own window to avoid stealing game focusif($pidOut-eq$myPid){$lastHwnd$hwndStart-Sleep-Milliseconds$PollIntervalMscontinue}$title$rawTitleif([string]::IsNullOrEmpty($title)){$title(no title)}if([string]::IsNullOrEmpty($procName)){$procNameunknown}$isDesktop($procName-eqexplorer-and($rawTitle-eq-or$rawTitle-eqProgram Manager))$eventCountif($isDesktop){# BACK TO DESKTOP - take snapshot$line [$now] BACK TO DESKTOP (explorer.exe)Write-Host$line-ForegroundColorRed[$now] BACK TO DESKTOP|Out-File-FilePath$LogFile-EncodingUTF8-AppendWrite-Host [$now] Snapshot: all visible windows ----ForegroundColorMagenta[$now] SNAPSHOT:|Out-File-FilePath$LogFile-EncodingUTF8-Append$snap[WinAPI2]::GetVisibleWindowSnapshot()foreach($entryin$snap){# Skip explorer desktop and our own processif($entry-matchProgram Manager \| explorer){continue}Write-Host$entry-ForegroundColorDarkYellow$entry|Out-File-FilePath$LogFile-EncodingUTF8-Append}Write-Host [$now] End snapshot-ForegroundColorMagenta---|Out-File-FilePath$LogFile-EncodingUTF8-AppendWrite-Host}else{$line [$now] FOCUS [$title] ($procName/ PID:$pidOut)Write-Host$line-ForegroundColorWhite[$now] FOCUS Title:$title| Process:$procName| PID:$pidOut|Out-File-FilePath$LogFile-EncodingUTF8-Append}$lastHwnd$hwnd}Start-Sleep-Milliseconds$PollIntervalMs}}finally{Write-HostWrite-Host -------------------------------------------------ForegroundColorDarkGray Write-Host Stopped.$eventCountevents recorded.-ForegroundColorYellow Write-Host Log:$LogFile-ForegroundColorYellow Write-Host}启动监视器.bat内容如下echo off chcp65001nul21title Window Monitor v2.0 echo.echoStarting Window Monitor... echo. powershell-NoProfile-ExecutionPolicyBypass-File%~dp0WindowMonitor.ps1pause
LOL切回桌面问题,采用监控抓出元凶方式
LOL 更新后偶尔在进入游戏时会自动跳回桌面。搜索后发现有人提到可以用监控软件查看是哪个应用进程抢占了窗口焦点。受此启发我编写了一个 PowerShell 脚本通过实时监控前台窗口切换来揪出元凶。使用方法1. 下载与准备将WindowMonitor.ps1和启动监视器.bat两个文件放在同一目录下例如桌面或任意文件夹。2. 启动监视器方式一推荐双击启动监视器.bat文件脚本会自动启动并打开 PowerShell 窗口。方式二右键WindowMonitor.ps1→ 选择「使用 PowerShell 运行」。3. 观察与记录启动后控制台窗口会实时显示每次窗口焦点切换的信息格式如下[21:30:15.123] FOCUS [LOL客户端] (LeagueClientUx / PID:12345) [21:30:18.456] FOCUS [Google Chrome] (chrome / PID:67890) [21:30:18.789] FOCUS BACK TO DESKTOP (explorer.exe)正常游戏时焦点应保持在游戏窗口。如果突然跳回桌面控制台会以红色显示BACK TO DESKTOP记录同时日志中会记录跳转前最后一个获得焦点的进程帮助定位元凶。4. 查看日志脚本会在同目录下自动生成日志文件命名格式为WindowMonitor_Log_20260613_213000.txt。日志记录了每次焦点切换的详细时间、窗口标题、进程名和 PID方便事后分析。5. 停止监视在 PowerShell 窗口中按Ctrl C即可停止监视控制台会显示本次共记录了多少次焦点切换事件。脚本内容WindowMonitor.ps1文件内容如下# 窗口活动监视器 v2.0 (便携版) 功能实时监控前台窗口切换记录每次焦点变化的时间、窗口标题和进程 用法双击 启动监视器.bat 或右键此文件用 PowerShell 运行 日志同目录下自动生成日志文件 #Add-Type using System; using System.Runtime.InteropServices; using System.Text; public class WinAPI { [DllImport(user32.dll)] public static extern IntPtr GetForegroundWindow(); [DllImport(user32.dll, CharSet CharSet.Auto, SetLastError true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport(user32.dll)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport(user32.dll)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport(user32.dll)] public static extern bool IsWindowVisible(IntPtr hWnd); } $PollIntervalMs 200$LogDirSplit-Path-Parent$MyInvocation.MyCommand.Path$tsGet-Date-FormatyyyyMMdd_HHmmss$LogFileJoin-Path$LogDirWindowMonitor_Log_$ts.txt$ShowDesktopAsFocus$truefunctionGet-ForegroundInfo{$hwnd[WinAPI]::GetForegroundWindow()if($hwnd-eq[IntPtr]::Zero){return$null}$len[WinAPI]::GetWindowTextLength($hwnd)$sbNew-ObjectSystem.Text.StringBuilder($len1)[WinAPI]::GetWindowText($hwnd,$sb,$sb.Capacity)|Out-Null$title$sb.ToString()$pidOut[uint32]0[WinAPI]::GetWindowThreadProcessId($hwnd,[ref]$pidOut)|Out-Null$procNametry{$procGet-Process-Id$pidOut-ErrorAction SilentlyContinueif($proc){$procName$proc.ProcessName}}catch{}return{Hwnd $hwndTitle $titleProcName $procNamePid $pidOut}}$Host.UI.RawUI.WindowTitle Window Monitor v2.0Write-HostWrite-Host -ForegroundColor CyanWrite-Host Window Monitor v2.0 (Portable)-ForegroundColor CyanWrite-Host -ForegroundColor CyanWrite-HostWrite-Host Log:$LogFile-ForegroundColor YellowWrite-Host Poll: ${PollIntervalMs}ms | CtrlC to stop-ForegroundColor YellowWrite-HostWrite-Host -------------------------------------------------ForegroundColor DarkGrayWrite-Host$headerWindow Monitor v2.0 LogrnStart:$(Get-Date-Formatyyyy-MM-dd HH:mm:ss)rnPoll: ${PollIntervalMs}msrnrn$header|Out-File-FilePath$LogFile-Encoding UTF8$lastHwnd[IntPtr]::Zero$eventCount 0try{while($true){$infoGet-ForegroundInfoif($null-ne$info-and$info.Hwnd-ne$lastHwnd){$nowGet-Date-FormatHH:mm:ss.fff$t$info.Title$p$info.ProcName$pidVal$info.Pidif([string]::IsNullOrEmpty($t)){$t(no title)}if([string]::IsNullOrEmpty($p)){$punknown}$isDesktop($p-eqexplorer-and($info.Title-eq-or$info.Title-eqProgram Manager))if($isDesktop-and-not$ShowDesktopAsFocus){Start-Sleep-Milliseconds$PollIntervalMscontinue}$eventCountif($isDesktop){$line [$now] BACK TO DESKTOP (explorer.exe)$colorRed}else{$line [$now] FOCUS [$t] ($p/ PID:$pidVal)$colorWhite}Write-Host$line-ForegroundColor$color$logLine[$now] FOCUS Title:$t| Process:$p| PID:$pidVal$logLine|Out-File-FilePath$LogFile-Encoding UTF8-Append$lastHwnd$info.Hwnd}Start-Sleep-Milliseconds$PollIntervalMs}}finally{Write-HostWrite-Host -------------------------------------------------ForegroundColor DarkGrayWrite-Host Stopped.$eventCountevents recorded.-ForegroundColor YellowWrite-Host Log:$LogFile-ForegroundColor YellowWrite-Host}WindowMonitor.ps1文件v2.1版本内容如下#Window Monitor v2.1(Portable - Hybrid Edition)- 50ms fast polling to catch quick popups - Auto snapshot of all visible windows when BACK TO DESKTOP detected - Monitor window minimized to avoid stealing game focus Usage: double-click the .bat launcher or run this .ps1inPowerShell#Add-Type using System; using System.Runtime.InteropServices; using System.Text; using System.Collections.Generic; public class WinAPI2 { [DllImport(user32.dll)] public static extern IntPtr GetForegroundWindow(); [DllImport(user32.dll, CharSet CharSet.Auto, SetLastError true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport(user32.dll)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport(user32.dll)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport(user32.dll)] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport(user32.dll)] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport(user32.dll)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport(user32.dll)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport(kernel32.dll)] public static extern IntPtr GetConsoleWindow(); public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; } public const int SW_MINIMIZE 6; public const int SW_SHOWMINNOACTIVE 7; public static void MinimizeConsole() { IntPtr console GetConsoleWindow(); if (console ! IntPtr.Zero) { ShowWindow(console, SW_MINIMIZE); } } public static Liststring GetVisibleWindowSnapshot() { var result new Liststring(); EnumWindows(delegate(IntPtr hWnd, IntPtr param) { if (IsWindowVisible(hWnd)) { int len GetWindowTextLength(hWnd); if (len 0) { StringBuilder sb new StringBuilder(len 1); GetWindowText(hWnd, sb, sb.Capacity); string title sb.ToString(); uint pid 0; GetWindowThreadProcessId(hWnd, out pid); string procName ;try{var pSystem.Diagnostics.Process.GetProcessById((int)pid);if(p!null)procNamep.ProcessName;} catch {} RECT rect;GetWindowRect(hWnd,out rect);int wrect.Right-rect.Left;int hrect.Bottom-rect.Top;if(w5h5procName!){ result.Add(string.Format({0}|{1}|PID:{2}|{3}x{4},title,procName,pid,w,h));}}}returntrue;}, IntPtr.Zero);returnresult;}}$PollIntervalMs 50$LogDir Split-Path -Parent$MyInvocation.MyCommand.Path$ts Get-Date -Format yyyyMMdd_HHmmss$LogFile Join-Path$LogDirWindowMonitor_Log_$ts.txt$Host.UI.RawUI.WindowTitle Window Monitor v2.1 Write-Host Write-Host -ForegroundColorCyan Write-Host Window Monitor v2.1 (Hybrid)-ForegroundColorCyan Write-Host -ForegroundColorCyan Write-HostWrite-Host Log:$LogFile-ForegroundColorYellow Write-Host Poll:${PollIntervalMs}ms desktop snapshot-ForegroundColorYellow Write-Host CtrlC to stop-ForegroundColorYellow Write-HostWrite-Host Monitor will minimize in 3 seconds...-ForegroundColorDarkGray Start-Sleep-Seconds3[WinAPI2]::MinimizeConsole()Write-HostWrite-Host -------------------------------------------------ForegroundColorDarkGray Write-Host$headerWindow Monitor v2.1 LogrnStart:$(Get-Date-Formatyyyy-MM-dd HH:mm:ss)rnPoll:${PollIntervalMs}ms snapshot on desktoprnrn$header|Out-File-FilePath$LogFile-EncodingUTF8$lastHwnd[IntPtr]::Zero$eventCount0$myPid$PIDtry{while($true){$hwnd[WinAPI2]::GetForegroundWindow()if($hwnd-ne[IntPtr]::Zero-and$hwnd-ne$lastHwnd){$nowGet-Date-FormatHH:mm:ss.fff$len[WinAPI2]::GetWindowTextLength($hwnd)$sbNew-Object System.Text.StringBuilder($len1)[WinAPI2]::GetWindowText($hwnd,$sb,$sb.Capacity)|Out-Null$rawTitle$sb.ToString()$pidOut[UInt32]0[WinAPI2]::GetWindowThreadProcessId($hwnd,[ref]$pidOut)|Out-Null$procNametry{$procGet-Process-Id$pidOut-ErrorActionSilentlyContinueif($proc){$procName$proc.ProcessName}}catch{}# Skip our own window to avoid stealing game focusif($pidOut-eq$myPid){$lastHwnd$hwndStart-Sleep-Milliseconds$PollIntervalMscontinue}$title$rawTitleif([string]::IsNullOrEmpty($title)){$title(no title)}if([string]::IsNullOrEmpty($procName)){$procNameunknown}$isDesktop($procName-eqexplorer-and($rawTitle-eq-or$rawTitle-eqProgram Manager))$eventCountif($isDesktop){# BACK TO DESKTOP - take snapshot$line [$now] BACK TO DESKTOP (explorer.exe)Write-Host$line-ForegroundColorRed[$now] BACK TO DESKTOP|Out-File-FilePath$LogFile-EncodingUTF8-AppendWrite-Host [$now] Snapshot: all visible windows ----ForegroundColorMagenta[$now] SNAPSHOT:|Out-File-FilePath$LogFile-EncodingUTF8-Append$snap[WinAPI2]::GetVisibleWindowSnapshot()foreach($entryin$snap){# Skip explorer desktop and our own processif($entry-matchProgram Manager \| explorer){continue}Write-Host$entry-ForegroundColorDarkYellow$entry|Out-File-FilePath$LogFile-EncodingUTF8-Append}Write-Host [$now] End snapshot-ForegroundColorMagenta---|Out-File-FilePath$LogFile-EncodingUTF8-AppendWrite-Host}else{$line [$now] FOCUS [$title] ($procName/ PID:$pidOut)Write-Host$line-ForegroundColorWhite[$now] FOCUS Title:$title| Process:$procName| PID:$pidOut|Out-File-FilePath$LogFile-EncodingUTF8-Append}$lastHwnd$hwnd}Start-Sleep-Milliseconds$PollIntervalMs}}finally{Write-HostWrite-Host -------------------------------------------------ForegroundColorDarkGray Write-Host Stopped.$eventCountevents recorded.-ForegroundColorYellow Write-Host Log:$LogFile-ForegroundColorYellow Write-Host}启动监视器.bat内容如下echo off chcp65001nul21title Window Monitor v2.0 echo.echoStarting Window Monitor... echo. powershell-NoProfile-ExecutionPolicyBypass-File%~dp0WindowMonitor.ps1pause