Files
genshin-assistant/scripts/capture-window.ps1
T

105 lines
4.0 KiB
PowerShell

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