# shot_dialog_anaf.ps1 - lanseaza shot_dialog_anaf.prg (dialog ANAF REAL, neinlocuit) si # captureaza fereastra nativa MESSAGEBOX() (clasa #32770) aparuta in acelasi proces vfp9, # apoi o inchide cu WM_CLOSE (fara SendKeys, fara sa fure focus - fereastra e mutata off-screen # inainte de captura, ca in vfp_ui_harness.ps1). param( [string]$Prg = (Join-Path $PSScriptRoot 'shot_dialog_anaf.prg'), [string]$OutPng = (Join-Path $PSScriptRoot 'screenshots\dialog_anaf_real.png'), [int]$ReadyTimeoutSec = 150, [int]$DialogTimeoutSec = 20, [string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' ) $ErrorActionPreference = 'Stop' $TestDir = [System.IO.Path]::GetDirectoryName($Prg) $TestFxp = [System.IO.Path]::ChangeExtension($Prg, 'fxp') $TestLog = Join-Path $TestDir 'shot_dialog_anaf_log.txt' $SyncDir = Join-Path $TestDir 'uisync_dialog' $Precompile = Join-Path (Split-Path $TestDir -Parent) '_precompile.ps1' Add-Type -AssemblyName System.Drawing Add-Type @" using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; public class Win32Dlg { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; } [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdc, uint nFlags); [DllImport("user32.dll")] public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); public static readonly IntPtr HWND_BOTTOM = new IntPtr(1); public const uint SWP_NOSIZE = 0x0001; public const uint SWP_NOACTIVATE = 0x0010; public const uint WM_CLOSE = 0x0010; public static List FindByPidAndClass(int pid, string className) { var result = new List(); EnumWindows((hWnd, lParam) => { int wpid; GetWindowThreadProcessId(hWnd, out wpid); if (wpid == pid && IsWindowVisible(hWnd)) { var sb = new StringBuilder(256); GetClassName(hWnd, sb, sb.Capacity); if (sb.ToString() == className) { result.Add(hWnd); } } return true; }, IntPtr.Zero); return result; } public static string GetTitle(IntPtr hWnd) { var sb = new StringBuilder(512); GetWindowText(hWnd, sb, sb.Capacity); return sb.ToString(); } } "@ function Push-Offscreen([IntPtr]$Hwnd) { if ($Hwnd -eq [IntPtr]::Zero -or -not [Win32Dlg]::IsWindow($Hwnd)) { return } [Win32Dlg]::SetWindowPos($Hwnd, [Win32Dlg]::HWND_BOTTOM, -4000, 0, 0, 0, ([Win32Dlg]::SWP_NOSIZE -bor [Win32Dlg]::SWP_NOACTIVATE)) | Out-Null } function Wait-MainWindowHandle([System.Diagnostics.Process]$Proc, [int]$TimeoutSec) { $deadline = (Get-Date).AddSeconds($TimeoutSec) while ((Get-Date) -lt $deadline) { try { $Proc.Refresh() if ($Proc.HasExited) { return [IntPtr]::Zero } if ($Proc.MainWindowHandle -ne [IntPtr]::Zero) { return $Proc.MainWindowHandle } } catch {} Start-Sleep -Milliseconds 200 } return [IntPtr]::Zero } function Wait-File([string]$Path, [int]$TimeoutSec, [IntPtr]$Hwnd = [IntPtr]::Zero) { $deadline = (Get-Date).AddSeconds($TimeoutSec) while (-not (Test-Path $Path)) { if ((Get-Date) -gt $deadline) { return $false } if ($Hwnd -ne [IntPtr]::Zero) { Push-Offscreen $Hwnd } Start-Sleep -Milliseconds 300 } return $true } function Take-Screenshot([IntPtr]$Hwnd, [string]$Path) { $rect = New-Object Win32Dlg+RECT [Win32Dlg]::GetWindowRect($Hwnd, [ref]$rect) | Out-Null $w = $rect.Right - $rect.Left $h = $rect.Bottom - $rect.Top if ($w -le 0 -or $h -le 0) { $w = 600; $h = 300 } $bmp = New-Object System.Drawing.Bitmap $w, $h $g = [System.Drawing.Graphics]::FromImage($bmp) $hdc = $g.GetHdc() $ok = [Win32Dlg]::PrintWindow($Hwnd, $hdc, 2) $g.ReleaseHdc($hdc) $g.Dispose() if (-not $ok) { $g2 = [System.Drawing.Graphics]::FromImage($bmp) $hdc2 = $g2.GetHdc() [Win32Dlg]::PrintWindow($Hwnd, $hdc2, 0) | Out-Null $g2.ReleaseHdc($hdc2) $g2.Dispose() } $bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png) $bmp.Dispose() } New-Item -ItemType Directory -Force -Path (Split-Path $OutPng -Parent) | Out-Null if (Test-Path $SyncDir) { Get-ChildItem $SyncDir -File | Remove-Item -Force -ErrorAction SilentlyContinue } else { New-Item -ItemType Directory -Path $SyncDir | Out-Null } Write-Host "Precompilez .fxp (izolat): $Precompile -Prg $Prg" Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 500 Start-Process powershell -WindowStyle Hidden -ArgumentList @('-ExecutionPolicy','Bypass','-File',"`"$Precompile`"",'-Prg',"`"$Prg`"") -Wait | Out-Null Start-Sleep -Seconds 2 Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 if (-not (Test-Path $TestFxp)) { Write-Warning "Nu s-a generat $TestFxp - compilare esuata."; exit 1 } $ready0 = Join-Path $SyncDir 'ready_0.txt' if (Test-Path $ready0) { Remove-Item $ready0 -Force } if (Test-Path $TestLog) { Remove-Item $TestLog -Force } $t0 = Get-Date Write-Host "Lansez VFP: $Vfp -A $TestFxp" $proc = Start-Process -FilePath $Vfp -ArgumentList @('-A', "`"$TestFxp`"") -PassThru $hwnd = Wait-MainWindowHandle $proc 15 Push-Offscreen $hwnd if (-not (Wait-File $ready0 $ReadyTimeoutSec $hwnd)) { Write-Warning "Nu am primit ready_0 in ${ReadyTimeoutSec}s. Ultima linie log: '$(Get-Content $TestLog -Tail 1 -ErrorAction SilentlyContinue)'" if (-not $proc.HasExited) { try { $proc.Kill() } catch {} } Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue exit 1 } Write-Host "ready_0 primit - astept fereastra MESSAGEBOX() nativa (clasa #32770, acelasi PID)" $dlgHwnd = [IntPtr]::Zero $deadline = (Get-Date).AddSeconds($DialogTimeoutSec) while ((Get-Date) -lt $deadline -and $dlgHwnd -eq [IntPtr]::Zero) { $cands = [Win32Dlg]::FindByPidAndClass($proc.Id, '#32770') foreach ($h in $cands) { $dlgHwnd = $h; break } if ($dlgHwnd -eq [IntPtr]::Zero) { Start-Sleep -Milliseconds 300 } } if ($dlgHwnd -eq [IntPtr]::Zero) { Write-Warning "Fereastra dialogului nu a aparut in ${DialogTimeoutSec}s." if (-not $proc.HasExited) { try { $proc.Kill() } catch {} } Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue exit 1 } Write-Host ("dialog gasit: hwnd={0} titlu=[{1}]" -f $dlgHwnd, [Win32Dlg]::GetTitle($dlgHwnd)) Push-Offscreen $dlgHwnd Start-Sleep -Milliseconds 600 Push-Offscreen $dlgHwnd Take-Screenshot $dlgHwnd $OutPng Write-Host "screenshot: $OutPng" # inchide dialogul (echivalent Escape/Cancel) - nu SendKeys, ca sa nu ceara focus [Win32Dlg]::PostMessage($dlgHwnd, [Win32Dlg]::WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null $done = Join-Path $SyncDir 'done.txt' if (Wait-File $done 30 $hwnd) { Write-Host "done: $((Get-Content $done -Raw).Trim())" } else { Write-Warning "Nu am primit done.txt dupa inchiderea dialogului." } Start-Sleep -Milliseconds 500 if (-not $proc.HasExited) { try { $proc.Kill() } catch {} } Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Write-Host "GATA. Screenshot in $OutPng"