param( [Parameter(Mandatory = $true)] [int]$ProcessId, [Parameter(Mandatory = $true)] [string]$OutputPath, [int]$ClickX = -1, [int]$ClickY = -1, [int]$WaitAfterClickMs = 700, [switch]$CloseAfterCapture ) $ErrorActionPreference = "Stop" trap { $errorPath = [IO.Path]::GetFullPath("$OutputPath.error.txt") New-Item -ItemType Directory -Force -Path (Split-Path -Parent $errorPath) | Out-Null [string]$_.Exception.Message | Set-Content -LiteralPath $errorPath -Encoding UTF8 exit 1 } Add-Type -TypeDefinition @' using System; using System.Runtime.InteropServices; public static class WindowCaptureNative { [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int command); [DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, uint flags); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] public static extern void mouse_event(uint flags, uint dx, uint dy, uint data, UIntPtr extraInfo); [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint message, UIntPtr wParam, IntPtr lParam); } '@ Add-Type -AssemblyName System.Drawing $process = Get-Process -Id $ProcessId -ErrorAction Stop $handle = [IntPtr]$process.MainWindowHandle if ($handle -eq [IntPtr]::Zero) { throw "Process $ProcessId has no main window." } [WindowCaptureNative]::ShowWindowAsync($handle, 9) | Out-Null [WindowCaptureNative]::SetForegroundWindow($handle) | Out-Null Start-Sleep -Milliseconds 700 $rect = New-Object WindowCaptureNative+RECT if (-not [WindowCaptureNative]::GetWindowRect($handle, [ref]$rect)) { throw "GetWindowRect failed." } $width = $rect.Right - $rect.Left $height = $rect.Bottom - $rect.Top if ($width -lt 1 -or $height -lt 1) { throw "Window dimensions are invalid: ${width}x${height}." } $clicked = $false if ($ClickX -ge 0 -or $ClickY -ge 0) { if ($ClickX -lt 0 -or $ClickY -lt 0 -or $ClickX -ge $width -or $ClickY -ge $height) { throw "Click coordinates must stay inside the target window." } if ([WindowCaptureNative]::GetForegroundWindow() -ne $handle) { throw "Target process is not foreground; no click was sent." } [WindowCaptureNative]::SetCursorPos($rect.Left + $ClickX, $rect.Top + $ClickY) | Out-Null [WindowCaptureNative]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) [WindowCaptureNative]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) $clicked = $true Start-Sleep -Milliseconds ([Math]::Max(0, $WaitAfterClickMs)) } $resolvedOutput = [IO.Path]::GetFullPath($OutputPath) $outputDirectory = Split-Path -Parent $resolvedOutput New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null $bitmap = New-Object System.Drawing.Bitmap $width, $height $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $printed = $false try { $hdc = $graphics.GetHdc() try { $printed = [WindowCaptureNative]::PrintWindow($handle, $hdc, 2) } finally { $graphics.ReleaseHdc($hdc) } if (-not $printed) { $graphics.CopyFromScreen($rect.Left, $rect.Top, 0, 0, $bitmap.Size) } $bitmap.Save($resolvedOutput, [System.Drawing.Imaging.ImageFormat]::Png) } finally { $graphics.Dispose() $bitmap.Dispose() } $closeRequested = $false if ($CloseAfterCapture) { $closeRequested = [WindowCaptureNative]::PostMessage($handle, 0x0010, [UIntPtr]::Zero, [IntPtr]::Zero) } [pscustomobject]@{ ok = $true processId = $ProcessId title = $process.MainWindowTitle width = $width height = $height printWindow = $printed clicked = $clicked closeRequested = $closeRequested outputPath = $resolvedOutput } | ConvertTo-Json