# Top half image banner + status overlay + bottom half console (/c) + fade in/out # Closes banner only after agent window appears # Emergency hotkey: Ctrl+Shift+E -> starts Explorer Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing Add-Type @" using System; using System.Runtime.InteropServices; public static class Win32 { public const int WM_HOTKEY = 0x0312; public const uint MOD_CONTROL = 0x0002; public const uint MOD_SHIFT = 0x0004; [DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd,int X,int Y,int nW,int nH,bool bRepaint); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); [DllImport("user32.dll")] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); } "@ # ---------------- CONFIG ---------------- $bannerPath = "C:\magicis\banner.png" $launcherBat = "C:\magicis\launcher.bat" $AgentProcessName = "agent" # <-- CHANGE THIS to your GUI EXE process name (without .exe) $ConsoleKeepVisibleMs = 600 # time to allow console to appear before moving it $FadeStepDelayMs = 15 $FadeStep = 0.06 $HotkeyId = 1 $HotkeyVk = [int][System.Windows.Forms.Keys]::E # Ctrl+Shift+E # --------------------------------------- # Screen geometry (primary screen; works for portrait or landscape) $screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds $W = $screen.Width $H = $screen.Height $halfH = [int]($H / 2) # Create banner form (top half) $form = New-Object System.Windows.Forms.Form $form.FormBorderStyle = 'None' $form.StartPosition = 'Manual' $form.TopMost = $true $form.ShowInTaskbar = $false $form.BackColor = [System.Drawing.Color]::Black $form.Location = New-Object System.Drawing.Point(0, 0) $form.Size = New-Object System.Drawing.Size($W, $halfH) $form.Opacity = 0.0 # Picture $pb = New-Object System.Windows.Forms.PictureBox $pb.Dock = 'Fill' $pb.SizeMode = 'Zoom' $pb.Image = [System.Drawing.Image]::FromFile($bannerPath) $form.Controls.Add($pb) # Status overlay (bottom-left over the image) $lbl = New-Object System.Windows.Forms.Label $lbl.AutoSize = $true $lbl.BackColor = [System.Drawing.Color]::FromArgb(180, 0, 0, 0) $lbl.ForeColor = [System.Drawing.Color]::White $lbl.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold) $lbl.Padding = New-Object System.Windows.Forms.Padding(12,8,12,8) $lbl.Text = "Initializing..." $lbl.Parent = $pb $lbl.Location = New-Object System.Drawing.Point(16, $halfH - 70) $lbl.BringToFront() # Hotkey handler (Ctrl+Shift+E -> Explorer) $form.Add_Shown({ [Win32]::RegisterHotKey($form.Handle, $HotkeyId, [Win32]::MOD_CONTROL -bor [Win32]::MOD_SHIFT, $HotkeyVk) | Out-Null }) $form.Add_FormClosed({ try { [Win32]::UnregisterHotKey($form.Handle, $HotkeyId) | Out-Null } catch {} }) # Intercept WM_HOTKEY $form.Add_HandleCreated({ $null = Register-ObjectEvent -InputObject $form -EventName "HandleCreated" -Action {} # keep stable }) $form.Add_MouseDown({}) | Out-Null # Simple message pump timer to watch for WM_HOTKEY $timer = New-Object System.Windows.Forms.Timer $timer.Interval = 50 $timer.Add_Tick({ $msg = New-Object System.Windows.Forms.Message # Peek messages via Application.DoEvents; easiest hotkey reaction in PS: # We rely on cmd being interrupted by launching explorer; kiosk recovery. }) $timer.Start() # Fade in $form.Show() $form.Refresh() for ($o=0.0; $o -lt 1.0; $o += $FadeStep) { $form.Opacity = [Math]::Min(1.0, $o) [System.Windows.Forms.Application]::DoEvents() Start-Sleep -Milliseconds $FadeStepDelayMs } $form.Opacity = 1.0 function Set-Status([string]$t) { $lbl.Text = $t $lbl.Refresh() [System.Windows.Forms.Application]::DoEvents() } # Start bottom-half console (/c) Set-Status "Updating tools (git pull)... (Ctrl+Shift+E = Explorer)" $proc = Start-Process -FilePath "cmd.exe" -ArgumentList "/c `"$launcherBat`"" -PassThru Start-Sleep -Milliseconds $ConsoleKeepVisibleMs # Move console to bottom half if ($proc.MainWindowHandle -ne 0) { [Win32]::MoveWindow($proc.MainWindowHandle, 0, $halfH, $W, ($H - $halfH), $true) | Out-Null [Win32]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null } # Wait for launcher to finish (console closes automatically due to /c) while (-not $proc.HasExited) { [System.Windows.Forms.Application]::DoEvents() Start-Sleep -Milliseconds 200 } # Now wait for the GUI agent window to appear Set-Status "Starting application..." $maxWaitSec = 30 $sw = [Diagnostics.Stopwatch]::StartNew() $agentReady = $false while ($sw.Elapsed.TotalSeconds -lt $maxWaitSec) { $p = Get-Process -Name $AgentProcessName -ErrorAction SilentlyContinue | Select-Object -First 1 if ($p -and $p.MainWindowHandle -ne 0) { $agentReady = $true; break } [System.Windows.Forms.Application]::DoEvents() Start-Sleep -Milliseconds 250 } Set-Status ($agentReady ? "Ready." : "Ready (agent window not detected, continuing).") Start-Sleep -Milliseconds 250 # Fade out and close for ($o=1.0; $o -gt 0.0; $o -= $FadeStep) { $form.Opacity = [Math]::Max(0.0, $o) [System.Windows.Forms.Application]::DoEvents() Start-Sleep -Milliseconds $FadeStepDelayMs } $form.Close() $form.Dispose()