Reorganize software catalog
This commit is contained in:
@@ -0,0 +1,534 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Executable,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$OutputDirectory,
|
||||
|
||||
[string]$Arguments = "",
|
||||
|
||||
[string]$WorkingDirectory = "",
|
||||
|
||||
[string]$InitialKeys = "",
|
||||
|
||||
[string]$ScreenshotTool = "",
|
||||
|
||||
[ValidateRange(1, 60)]
|
||||
[int]$WaitSeconds = 8
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class ScreenshotWindowApi {
|
||||
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
[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 EnumWindows(EnumWindowsProc callback, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, out Rect rect);
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
public static extern int DwmGetWindowAttribute(IntPtr hWnd, int attribute, out Rect value, int size);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool IsIconic(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool IsZoomed(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool ShowWindow(IntPtr hWnd, int command);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr insertAfter, int x, int y, int width, int height, uint flags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool PostMessage(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
}
|
||||
"@
|
||||
|
||||
function Close-StaleWindows {
|
||||
[ScreenshotWindowApi]::EnumWindows({
|
||||
param($handle, $parameter)
|
||||
|
||||
if ([ScreenshotWindowApi]::IsWindowVisible($handle)) {
|
||||
$title = [System.Text.StringBuilder]::new(1024)
|
||||
[void][ScreenshotWindowApi]::GetWindowText($handle, $title, $title.Capacity)
|
||||
if ($title.ToString() -match '^slged\.exe\s+-') {
|
||||
[void][ScreenshotWindowApi]::PostMessage($handle, 0x0010, [IntPtr]::Zero, [IntPtr]::Zero)
|
||||
}
|
||||
}
|
||||
return $true
|
||||
}, [IntPtr]::Zero) | Out-Null
|
||||
}
|
||||
|
||||
function Get-ProcessTreeIds {
|
||||
param([int]$RootProcessId)
|
||||
|
||||
$ids = [System.Collections.Generic.HashSet[int]]::new()
|
||||
[void]$ids.Add($RootProcessId)
|
||||
|
||||
do {
|
||||
$changed = $false
|
||||
foreach ($process in Get-CimInstance Win32_Process) {
|
||||
if ($ids.Contains([int]$process.ParentProcessId) -and $ids.Add([int]$process.ProcessId)) {
|
||||
$changed = $true
|
||||
}
|
||||
}
|
||||
} while ($changed)
|
||||
|
||||
return ,$ids
|
||||
}
|
||||
|
||||
function Get-ProcessIds {
|
||||
$ids = [System.Collections.Generic.HashSet[int]]::new()
|
||||
foreach ($item in Get-Process -ErrorAction SilentlyContinue) {
|
||||
[void]$ids.Add([int]$item.Id)
|
||||
}
|
||||
return ,$ids
|
||||
}
|
||||
|
||||
function Stop-NewProcesses {
|
||||
param([System.Collections.Generic.HashSet[int]]$BaselineProcessIds)
|
||||
|
||||
$currentSessionId = (Get-Process -Id $PID).SessionId
|
||||
$protectedNames = @(
|
||||
"csrss", "dwm", "explorer", "fontdrvhost", "powershell", "sihost",
|
||||
"StartMenuExperienceHost", "taskhostw", "TextInputHost", "winlogon"
|
||||
)
|
||||
|
||||
foreach ($item in Get-Process -ErrorAction SilentlyContinue) {
|
||||
if (
|
||||
$item.Id -ne $PID -and
|
||||
$item.SessionId -eq $currentSessionId -and
|
||||
-not $BaselineProcessIds.Contains([int]$item.Id) -and
|
||||
$protectedNames -notcontains $item.ProcessName
|
||||
) {
|
||||
taskkill.exe /PID $item.Id /T /F 2>$null | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Stop-StaleCatalogProcesses {
|
||||
param([string]$ExecutablePath)
|
||||
|
||||
$match = [regex]::Match($ExecutablePath, '^(.*\\work)\\', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
||||
if (-not $match.Success) {
|
||||
return
|
||||
}
|
||||
|
||||
$workRoot = $match.Groups[1].Value.TrimEnd('\') + '\'
|
||||
foreach ($item in Get-CimInstance Win32_Process -ErrorAction SilentlyContinue) {
|
||||
if ($item.ExecutablePath -and $item.ExecutablePath.StartsWith($workRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
taskkill.exe /PID $item.ProcessId /T /F 2>$null | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ProgramWindows {
|
||||
param([System.Collections.Generic.HashSet[int]]$ProcessIds)
|
||||
|
||||
$windows = [System.Collections.Generic.List[object]]::new()
|
||||
[ScreenshotWindowApi]::EnumWindows({
|
||||
param($handle, $parameter)
|
||||
|
||||
if (-not [ScreenshotWindowApi]::IsWindowVisible($handle)) {
|
||||
return $true
|
||||
}
|
||||
|
||||
[uint32]$windowProcessId = 0
|
||||
[void][ScreenshotWindowApi]::GetWindowThreadProcessId($handle, [ref]$windowProcessId)
|
||||
if (-not $ProcessIds.Contains([int]$windowProcessId)) {
|
||||
return $true
|
||||
}
|
||||
|
||||
$title = [System.Text.StringBuilder]::new(1024)
|
||||
[void][ScreenshotWindowApi]::GetWindowText($handle, $title, $title.Capacity)
|
||||
|
||||
$fullRect = [ScreenshotWindowApi+Rect]::new()
|
||||
if (-not [ScreenshotWindowApi]::GetWindowRect($handle, [ref]$fullRect)) {
|
||||
return $true
|
||||
}
|
||||
|
||||
$rect = [ScreenshotWindowApi+Rect]::new()
|
||||
$dwmResult = [ScreenshotWindowApi]::DwmGetWindowAttribute(
|
||||
$handle,
|
||||
9,
|
||||
[ref]$rect,
|
||||
[System.Runtime.InteropServices.Marshal]::SizeOf([type][ScreenshotWindowApi+Rect])
|
||||
)
|
||||
if ($dwmResult -ne 0) {
|
||||
$rect = $fullRect
|
||||
}
|
||||
|
||||
$width = $rect.Right - $rect.Left
|
||||
$height = $rect.Bottom - $rect.Top
|
||||
if ($width -le 1 -or $height -le 1) {
|
||||
return $true
|
||||
}
|
||||
|
||||
$windows.Add([pscustomobject]@{
|
||||
Handle = $handle
|
||||
ProcessId = [int]$windowProcessId
|
||||
Title = $title.ToString()
|
||||
Left = $rect.Left
|
||||
Top = $rect.Top
|
||||
Width = $width
|
||||
Height = $height
|
||||
Area = $width * $height
|
||||
CropLeft = $rect.Left - $fullRect.Left
|
||||
CropTop = $rect.Top - $fullRect.Top
|
||||
})
|
||||
return $true
|
||||
}, [IntPtr]::Zero) | Out-Null
|
||||
|
||||
return $windows
|
||||
}
|
||||
|
||||
function Crop-WindowShadow {
|
||||
param(
|
||||
[string]$Path,
|
||||
[int]$Left,
|
||||
[int]$Top,
|
||||
[int]$Width,
|
||||
[int]$Height
|
||||
)
|
||||
|
||||
$source = [System.Drawing.Bitmap]::FromFile($Path)
|
||||
try {
|
||||
if ($Left -eq 0 -and $Top -eq 0 -and $source.Width -eq $Width -and $source.Height -eq $Height) {
|
||||
return
|
||||
}
|
||||
if ($Left -lt 0 -or $Top -lt 0 -or $Left + $Width -gt $source.Width -or $Top + $Height -gt $source.Height) {
|
||||
return
|
||||
}
|
||||
|
||||
$cropped = [System.Drawing.Bitmap]::new($Width, $Height, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
||||
$graphics = [System.Drawing.Graphics]::FromImage($cropped)
|
||||
try {
|
||||
$destination = [System.Drawing.Rectangle]::new(0, 0, $Width, $Height)
|
||||
$graphics.DrawImage($source, $destination, $Left, $Top, $Width, $Height, [System.Drawing.GraphicsUnit]::Pixel)
|
||||
$temporaryPath = "$Path.cropped.png"
|
||||
$cropped.Save($temporaryPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
}
|
||||
finally {
|
||||
$graphics.Dispose()
|
||||
$cropped.Dispose()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$source.Dispose()
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $temporaryPath -Destination $Path -Force
|
||||
}
|
||||
|
||||
function Clear-LowAlphaBorder {
|
||||
param(
|
||||
[string]$Path,
|
||||
[byte]$AlphaThreshold = 51,
|
||||
[int]$BorderWidth = 16
|
||||
)
|
||||
|
||||
$bitmap = [System.Drawing.Bitmap]::FromFile($Path)
|
||||
try {
|
||||
$horizontalBorder = [Math]::Min($BorderWidth, [Math]::Floor($bitmap.Height / 2))
|
||||
$verticalBorder = [Math]::Min($BorderWidth, [Math]::Floor($bitmap.Width / 2))
|
||||
$rectangle = [System.Drawing.Rectangle]::new(0, 0, $bitmap.Width, $bitmap.Height)
|
||||
$bitmapData = $bitmap.LockBits(
|
||||
$rectangle,
|
||||
[System.Drawing.Imaging.ImageLockMode]::ReadWrite,
|
||||
[System.Drawing.Imaging.PixelFormat]::Format32bppArgb
|
||||
)
|
||||
try {
|
||||
$stride = [Math]::Abs($bitmapData.Stride)
|
||||
$pixels = [byte[]]::new($stride * $bitmap.Height)
|
||||
[System.Runtime.InteropServices.Marshal]::Copy($bitmapData.Scan0, $pixels, 0, $pixels.Length)
|
||||
|
||||
for ($y = 0; $y -lt $bitmap.Height; $y++) {
|
||||
$isHorizontalBorder = $y -lt $horizontalBorder -or $y -ge $bitmap.Height - $horizontalBorder
|
||||
for ($x = 0; $x -lt $bitmap.Width; $x++) {
|
||||
if (-not $isHorizontalBorder -and $x -ge $verticalBorder -and $x -lt $bitmap.Width - $verticalBorder) {
|
||||
continue
|
||||
}
|
||||
|
||||
$alphaOffset = $y * $stride + $x * 4 + 3
|
||||
if ($pixels[$alphaOffset] -lt $AlphaThreshold) {
|
||||
$pixels[$alphaOffset] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Runtime.InteropServices.Marshal]::Copy($pixels, 0, $bitmapData.Scan0, $pixels.Length)
|
||||
}
|
||||
finally {
|
||||
$bitmap.UnlockBits($bitmapData)
|
||||
}
|
||||
|
||||
$temporaryPath = "$Path.alpha.png"
|
||||
$bitmap.Save($temporaryPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
}
|
||||
finally {
|
||||
$bitmap.Dispose()
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $temporaryPath -Destination $Path -Force
|
||||
}
|
||||
|
||||
function Save-Result {
|
||||
param([hashtable]$Result)
|
||||
|
||||
$Result | ConvertTo-Json -Depth 5 | Set-Content -Encoding UTF8 (Join-Path $OutputDirectory "$Name.json")
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutputDirectory | Out-Null
|
||||
$process = $null
|
||||
$baselineProcessIds = $null
|
||||
|
||||
try {
|
||||
Stop-StaleCatalogProcesses -ExecutablePath $Executable
|
||||
Close-StaleWindows
|
||||
$baselineProcessIds = Get-ProcessIds
|
||||
$startParameters = @{
|
||||
FilePath = $Executable
|
||||
WorkingDirectory = $(if ($WorkingDirectory.Length -gt 0) { $WorkingDirectory } else { Split-Path $Executable })
|
||||
PassThru = $true
|
||||
}
|
||||
if ($Arguments.Length -gt 0) {
|
||||
$startParameters.ArgumentList = $Arguments
|
||||
}
|
||||
$process = Start-Process @startParameters
|
||||
$deadline = (Get-Date).AddSeconds($WaitSeconds)
|
||||
$windows = @()
|
||||
|
||||
do {
|
||||
Start-Sleep -Milliseconds 500
|
||||
$processIds = Get-ProcessTreeIds -RootProcessId $process.Id
|
||||
$windows = @(Get-ProgramWindows -ProcessIds $processIds)
|
||||
} while ($windows.Count -eq 0 -and (Get-Date) -lt $deadline)
|
||||
|
||||
if ($windows.Count -eq 0) {
|
||||
$reason = if ($process.HasExited) { "process-exited" } else { "no-window" }
|
||||
Save-Result @{
|
||||
success = $false
|
||||
reason = $reason
|
||||
exitCode = $(if ($process.HasExited) { $process.ExitCode } else { $null })
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
$settleDeadline = (Get-Date).AddSeconds(3)
|
||||
do {
|
||||
Start-Sleep -Milliseconds 500
|
||||
$processIds = Get-ProcessTreeIds -RootProcessId $process.Id
|
||||
$settledWindows = @(Get-ProgramWindows -ProcessIds $processIds)
|
||||
if ($settledWindows.Count -gt 0) {
|
||||
$windows = $settledWindows
|
||||
}
|
||||
} while ((Get-Date) -lt $settleDeadline)
|
||||
|
||||
$preferredTitle = ""
|
||||
if ($InitialKeys.Length -gt 0) {
|
||||
foreach ($initialKey in $InitialKeys.Split(',')) {
|
||||
$rawKey = $initialKey.Trim()
|
||||
$key = $rawKey.ToUpperInvariant()
|
||||
if ($key.StartsWith("SELECT-TITLE=")) {
|
||||
$preferredTitle = $rawKey.Substring(13)
|
||||
}
|
||||
elseif ($key.StartsWith("TEXT=")) {
|
||||
$initialWindow = $windows | Sort-Object Area -Descending | Select-Object -First 1
|
||||
[void][ScreenshotWindowApi]::SetForegroundWindow($initialWindow.Handle)
|
||||
[System.Windows.Forms.SendKeys]::SendWait($rawKey.Substring(5))
|
||||
}
|
||||
elseif ($key -eq "CLOSE-SMALL") {
|
||||
$dialog = $windows | Sort-Object Area | Select-Object -First 1
|
||||
[void][ScreenshotWindowApi]::PostMessage($dialog.Handle, 0x0010, [IntPtr]::Zero, [IntPtr]::Zero)
|
||||
}
|
||||
elseif ($key -eq "CLOSE-LARGEST") {
|
||||
$dialog = $windows | Sort-Object Area -Descending | Select-Object -First 1
|
||||
[void][ScreenshotWindowApi]::PostMessage($dialog.Handle, 0x0010, [IntPtr]::Zero, [IntPtr]::Zero)
|
||||
}
|
||||
else {
|
||||
$initialWindow = $windows | Sort-Object Area -Descending | Select-Object -First 1
|
||||
[void][ScreenshotWindowApi]::SetForegroundWindow($initialWindow.Handle)
|
||||
$sendKeys = switch ($key) {
|
||||
"ALT+C" { "%c" }
|
||||
"ALT+N" { "%n" }
|
||||
"ALT+Y" { "%y" }
|
||||
"ALT+F4" { "%{F4}" }
|
||||
default { "{$key}" }
|
||||
}
|
||||
[System.Windows.Forms.SendKeys]::SendWait($sendKeys)
|
||||
}
|
||||
Start-Sleep -Seconds 2
|
||||
$processIds = Get-ProcessTreeIds -RootProcessId $process.Id
|
||||
$dismissedWindows = @(Get-ProgramWindows -ProcessIds $processIds)
|
||||
if ($dismissedWindows.Count -gt 0) {
|
||||
$windows = $dismissedWindows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$window = if ($preferredTitle.Length -gt 0) {
|
||||
$windows | Where-Object Title -Like "*$preferredTitle*" | Sort-Object Area -Descending | Select-Object -First 1
|
||||
}
|
||||
else {
|
||||
$windows | Sort-Object Area -Descending | Select-Object -First 1
|
||||
}
|
||||
if ($null -eq $window) {
|
||||
throw "No window matched preferred title: $preferredTitle"
|
||||
}
|
||||
if ([ScreenshotWindowApi]::IsIconic($window.Handle)) {
|
||||
[void][ScreenshotWindowApi]::ShowWindow($window.Handle, 9)
|
||||
}
|
||||
if (-not [ScreenshotWindowApi]::IsZoomed($window.Handle)) {
|
||||
[void][ScreenshotWindowApi]::SetWindowPos($window.Handle, [IntPtr]::Zero, 20, 20, 0, 0, 0x0015)
|
||||
Start-Sleep -Milliseconds 300
|
||||
$processIds = Get-ProcessTreeIds -RootProcessId $process.Id
|
||||
$movedWindows = @(Get-ProgramWindows -ProcessIds $processIds)
|
||||
$movedWindow = $movedWindows | Where-Object Handle -eq $window.Handle | Select-Object -First 1
|
||||
if ($null -ne $movedWindow) {
|
||||
$window = $movedWindow
|
||||
$windows = $movedWindows
|
||||
}
|
||||
}
|
||||
[void][ScreenshotWindowApi]::SetForegroundWindow($window.Handle)
|
||||
Close-StaleWindows
|
||||
Start-Sleep -Milliseconds 500
|
||||
|
||||
$screenshotPath = Join-Path $OutputDirectory "$Name.png"
|
||||
$borderInset = 2
|
||||
if ($ScreenshotTool.Length -gt 0 -and (Test-Path -LiteralPath $ScreenshotTool)) {
|
||||
$screenshotToolName = [System.IO.Path]::GetFileName($ScreenshotTool)
|
||||
if ($screenshotToolName -ieq "winapp.exe") {
|
||||
$winappExitCode = 1
|
||||
$winappOutput = @()
|
||||
for ($attempt = 1; $attempt -le 2; $attempt++) {
|
||||
Remove-Item -LiteralPath $screenshotPath -Force -ErrorAction SilentlyContinue
|
||||
$winappOutput = @(& $ScreenshotTool ui screenshot -w $window.Handle.ToInt64() --output $screenshotPath --json 2>&1)
|
||||
$winappExitCode = $LASTEXITCODE
|
||||
if ($winappExitCode -eq 0) {
|
||||
break
|
||||
}
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
if ($winappExitCode -ne 0) {
|
||||
Remove-Item -LiteralPath $screenshotPath -Force -ErrorAction SilentlyContinue
|
||||
$winappOutput = @(& $ScreenshotTool ui screenshot -a $window.ProcessId --output $screenshotPath --json 2>&1)
|
||||
$winappExitCode = $LASTEXITCODE
|
||||
}
|
||||
if ($winappExitCode -ne 0) {
|
||||
$winappDetails = ($winappOutput | Out-String).Trim()
|
||||
throw "winapp CLI failed for window '$($window.Title)' with exit code ${winappExitCode}: $winappDetails"
|
||||
}
|
||||
}
|
||||
else {
|
||||
[void][ScreenshotWindowApi]::SetForegroundWindow($window.Handle)
|
||||
Start-Sleep -Milliseconds 200
|
||||
& $ScreenshotTool savescreenshotwin $screenshotPath
|
||||
}
|
||||
$screenshotDeadline = (Get-Date).AddSeconds(3)
|
||||
while (
|
||||
(-not (Test-Path -LiteralPath $screenshotPath) -or (Get-Item -LiteralPath $screenshotPath).Length -eq 0) -and
|
||||
(Get-Date) -lt $screenshotDeadline
|
||||
) {
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $screenshotPath) -or (Get-Item -LiteralPath $screenshotPath).Length -eq 0) {
|
||||
throw "Window screenshot tool failed for window '$($window.Title)'."
|
||||
}
|
||||
if ($screenshotToolName -ieq "winapp.exe") {
|
||||
Clear-LowAlphaBorder -Path $screenshotPath
|
||||
}
|
||||
else {
|
||||
Crop-WindowShadow `
|
||||
-Path $screenshotPath `
|
||||
-Left ($window.CropLeft + $borderInset) `
|
||||
-Top ($window.CropTop + $borderInset) `
|
||||
-Width ($window.Width - 2 * $borderInset) `
|
||||
-Height ($window.Height - 2 * $borderInset)
|
||||
}
|
||||
}
|
||||
else {
|
||||
$bitmap = [System.Drawing.Bitmap]::new($window.Width, $window.Height, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
||||
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
||||
try {
|
||||
$graphics.CopyFromScreen($window.Left, $window.Top, 0, 0, $bitmap.Size)
|
||||
$bitmap.Save($screenshotPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
}
|
||||
finally {
|
||||
$graphics.Dispose()
|
||||
$bitmap.Dispose()
|
||||
}
|
||||
Crop-WindowShadow `
|
||||
-Path $screenshotPath `
|
||||
-Left $borderInset `
|
||||
-Top $borderInset `
|
||||
-Width ($window.Width - 2 * $borderInset) `
|
||||
-Height ($window.Height - 2 * $borderInset)
|
||||
}
|
||||
|
||||
Save-Result @{
|
||||
success = $true
|
||||
title = $window.Title
|
||||
width = $window.Width
|
||||
height = $window.Height
|
||||
windows = @($windows | ForEach-Object {
|
||||
@{
|
||||
processId = $_.ProcessId
|
||||
title = $_.Title
|
||||
width = $_.Width
|
||||
height = $_.Height
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Save-Result @{
|
||||
success = $false
|
||||
reason = "error"
|
||||
error = $_.Exception.Message
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
if ($null -ne $process -and -not $process.HasExited) {
|
||||
taskkill.exe /PID $process.Id /T /F 2>$null | Out-Null
|
||||
}
|
||||
if ($null -ne $baselineProcessIds) {
|
||||
Stop-NewProcesses -BaselineProcessIds $baselineProcessIds
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user