# Hands-on verification of TranslucentTB on a real Windows machine (GitHub # Actions windows-2025 = Windows Server 2025, build 26100, the Windows 11 24H2 # shell). Installs the official 2026.2 release, applies settings, and saves # real screenshots, logs and measurements to verify/results/. $ErrorActionPreference = 'Continue' $ProgressPreference = 'SilentlyContinue' $root = Split-Path -Parent $MyInvocation.MyCommand.Path $out = Join-Path $root 'results' New-Item -ItemType Directory -Force -Path $out | Out-Null $log = Join-Path $out 'run-log.txt' $facts = [ordered]@{} function Log($m) { $line = "[{0:HH:mm:ss}] {1}" -f (Get-Date), $m; Write-Host $line; Add-Content -Path $log -Value $line } Add-Type -AssemblyName System.Windows.Forms, System.Drawing, UIAutomationClient, UIAutomationTypes Add-Type @" using System; using System.Runtime.InteropServices; public static class N { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int SystemParametersInfo(int a, int b, string c, int d); [DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte scan, int flags, IntPtr extra); [DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] public static extern void mouse_event(int f, int x, int y, int d, IntPtr e); [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr FindWindow(string cls, string title); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int cmd); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; } } "@ [N]::SetProcessDPIAware() | Out-Null function Shot($name) { try { $b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds $bmp = New-Object System.Drawing.Bitmap $b.Width, $b.Height $g = [System.Drawing.Graphics]::FromImage($bmp) $g.CopyFromScreen($b.Location, [System.Drawing.Point]::Empty, $b.Size) $bmp.Save((Join-Path $out "$name.png"), [System.Drawing.Imaging.ImageFormat]::Png) # Taskbar close-up: bottom 120 px at 2x. $h = [Math]::Min(120, $b.Height) $crop = $bmp.Clone((New-Object System.Drawing.Rectangle 0, ($b.Height - $h), $b.Width, $h), $bmp.PixelFormat) $big = New-Object System.Drawing.Bitmap ($crop.Width), ($crop.Height * 2) $g2 = [System.Drawing.Graphics]::FromImage($big) $g2.InterpolationMode = 'NearestNeighbor' $g2.DrawImage($crop, 0, 0, $crop.Width, $crop.Height * 2) $big.Save((Join-Path $out "$name-taskbar.png"), [System.Drawing.Imaging.ImageFormat]::Png) $g.Dispose(); $g2.Dispose(); $bmp.Dispose(); $crop.Dispose(); $big.Dispose() Log "screenshot $name ($($b.Width)x$($b.Height))" } catch { Log "screenshot $name FAILED: $_" } } function Key([byte[]]$keys) { foreach ($k in $keys) { [N]::keybd_event($k, 0, 0, [IntPtr]::Zero); Start-Sleep -Milliseconds 40 } [array]::Reverse($keys) foreach ($k in $keys) { [N]::keybd_event($k, 0, 2, [IntPtr]::Zero); Start-Sleep -Milliseconds 40 } } $UIA = [System.Windows.Automation.AutomationElement] function FindName($name, $timeout = 20) { $cond = New-Object System.Windows.Automation.PropertyCondition($UIA::NameProperty, $name) $end = (Get-Date).AddSeconds($timeout) while ((Get-Date) -lt $end) { $el = $UIA::RootElement.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $cond) if ($el) { return $el } Start-Sleep -Milliseconds 700 } return $null } function Invoke-El($el) { try { $el.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern).Invoke(); return $true } catch { } try { $r = $el.Current.BoundingRectangle; Click ([int]($r.X + $r.Width / 2)) ([int]($r.Y + $r.Height / 2)) $false; return $true } catch { return $false } } function Click($x, $y, $right) { [N]::SetCursorPos($x, $y) | Out-Null; Start-Sleep -Milliseconds 150 if ($right) { [N]::mouse_event(0x08, 0, 0, 0, [IntPtr]::Zero); [N]::mouse_event(0x10, 0, 0, 0, [IntPtr]::Zero) } else { [N]::mouse_event(0x02, 0, 0, 0, [IntPtr]::Zero); [N]::mouse_event(0x04, 0, 0, 0, [IntPtr]::Zero) } } # ── 1. Machine ─────────────────────────────────────────────────────────── Log 'Machine info' $os = Get-CimInstance Win32_OperatingSystem $cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $facts.os = "$($os.Caption) | build $($cv.CurrentBuild).$($cv.UBR) | DisplayVersion $($cv.DisplayVersion)" $facts.session = "pwsh session $((Get-Process -Id $PID).SessionId); explorer sessions: " + ((Get-Process explorer -ErrorAction SilentlyContinue | ForEach-Object { $_.SessionId }) -join ',') $facts.screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.ToString() $tb = [N]::FindWindow('Shell_TrayWnd', $null) $facts.taskbarWindow = if ($tb -ne [IntPtr]::Zero) { 'Shell_TrayWnd found' } else { 'Shell_TrayWnd NOT found' } $facts.taskbarDllLoaded = [bool](Get-Process explorer -ErrorAction SilentlyContinue | ForEach-Object { $_.Modules } | Where-Object { $_.ModuleName -ieq 'Taskbar.dll' }) $facts | ConvertTo-Json | Set-Content (Join-Path $out 'machine.json') Log ($facts | ConvertTo-Json) # Minimize the runner's console so it does not cover the desktop. Get-Process WindowsTerminal, conhost, OpenConsole -ErrorAction SilentlyContinue | Where-Object MainWindowHandle -ne 0 | ForEach-Object { [N]::ShowWindow($_.MainWindowHandle, 6) | Out-Null } # Windows "Transparency effects" on, so acrylic can render (it is off on many VMs). Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize' -Name EnableTransparency -Value 1 -Type DWord -ErrorAction SilentlyContinue $facts.enableTransparency = (Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize' -ErrorAction SilentlyContinue).EnableTransparency $facts.gpu = (Get-CimInstance Win32_VideoController | ForEach-Object { $_.Name }) -join '; ' # ── 2. Wallpaper with strong shapes so translucency is visible ────────── $wp = Join-Path $env:TEMP 'ttb-wallpaper.png' $b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds $bmp = New-Object System.Drawing.Bitmap $b.Width, $b.Height $g = [System.Drawing.Graphics]::FromImage($bmp) $g.SmoothingMode = 'AntiAlias' $g.Clear([System.Drawing.Color]::FromArgb(13, 20, 36)) $shapes = @(@(-0.08, 0.45, 0.42, 240, 138, 75), @(0.62, -0.25, 0.40, 76, 199, 176), @(0.40, 0.70, 0.26, 244, 211, 94), @(0.80, 0.72, 0.18, 232, 234, 240), @(0.20, 0.82, 0.12, 109, 141, 255)) foreach ($s in $shapes) { $d = [int]($s[2] * $b.Width) $br = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb($s[3], $s[4], $s[5])) $g.FillEllipse($br, [int]($s[0] * $b.Width), [int]($s[1] * $b.Height), $d, $d) } $font = New-Object System.Drawing.Font('Segoe UI', 28, [System.Drawing.FontStyle]::Bold) $g.DrawString('translucenttb.dev test wallpaper', $font, [System.Drawing.Brushes]::White, 40, 40) $stripes = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::White) for ($x = 0; $x -lt $b.Width; $x += 60) { $g.FillRectangle($stripes, $x, $b.Height - 70, 20, 70) } $bmp.Save($wp, [System.Drawing.Imaging.ImageFormat]::Png); $g.Dispose(); $bmp.Dispose() [N]::SystemParametersInfo(20, 0, $wp, 3) | Out-Null Start-Sleep 3 Shot '00-before-translucenttb' # ── 3. Download official files, hashes, signatures ────────────────────── $rel = 'https://github.com/TranslucentTB/TranslucentTB/releases/download' $dl = Join-Path $env:TEMP 'ttb' New-Item -ItemType Directory -Force -Path $dl | Out-Null $files = @( @('2026.2', 'bundle.msixbundle'), @('2026.2', 'TranslucentTB-portable-x64.zip'), @('2026.2', 'TranslucentTB.appinstaller'), @('2024.3', 'vclibs-desktop-x64.appx'), @('2024.3', 'vclibs-uwp-x64.appx'), @('2026.1', 'winui-x64.appx')) $hashes = @() foreach ($f in $files) { $p = Join-Path $dl $f[1] Invoke-WebRequest "$rel/$($f[0])/$($f[1])" -OutFile $p $h = Get-FileHash $p -Algorithm SHA256 $hashes += [ordered]@{ file = $f[1]; release = $f[0]; size = (Get-Item $p).Length; sha256 = $h.Hash.ToLower() } Log "downloaded $($f[1]) $($h.Hash)" } $hashes | ConvertTo-Json | Set-Content (Join-Path $out 'hashes.json') # ── 4. Install the MSIX bundle (same package as the Store) ────────────── try { Add-AppxPackage -Path (Join-Path $dl 'bundle.msixbundle') -DependencyPath (Join-Path $dl 'vclibs-desktop-x64.appx'), (Join-Path $dl 'vclibs-uwp-x64.appx'), (Join-Path $dl 'winui-x64.appx') -ErrorAction Stop Log 'Add-AppxPackage OK' } catch { Log "Add-AppxPackage FAILED: $_" } $pkg = Get-AppxPackage *TranslucentTB* $pkg | Select-Object Name, Version, Architecture, PackageFamilyName, PackageFullName, InstallLocation, SignatureKind | Format-List | Out-String | Set-Content (Join-Path $out 'package.txt') Log ("package: " + ($pkg | Select-Object -ExpandProperty PackageFamilyName)) if ($pkg) { $sig = Get-AuthenticodeSignature (Join-Path $pkg.InstallLocation 'TranslucentTB.exe') "Status: $($sig.Status)`nSigner: $($sig.SignerCertificate.Subject)`nIssuer: $($sig.SignerCertificate.Issuer)`nTimestamp: $($sig.TimeStamperCertificate.Subject)" | Set-Content (Join-Path $out 'signature.txt') (Get-Item (Join-Path $pkg.InstallLocation 'TranslucentTB.exe')).VersionInfo | Format-List FileVersion, ProductVersion, CompanyName, FileDescription | Out-String | Add-Content (Join-Path $out 'signature.txt') Get-AppxPackageManifest $pkg | ForEach-Object { $_.Package.Applications.Application.Extensions.InnerXml } | Set-Content (Join-Path $out 'manifest-extensions.xml') } try { winget --version | Out-String | Set-Content (Join-Path $out 'winget.txt'); winget show --id CharlesMilette.TranslucentTB --accept-source-agreements --disable-interactivity 2>&1 | Out-String | Add-Content (Join-Path $out 'winget.txt') } catch { 'winget not available on this runner' | Set-Content (Join-Path $out 'winget.txt') } $pfn = '28017CharlesMilette.TranslucentTB_v826wp6bftszj' $state = Join-Path $env:LOCALAPPDATA "Packages\$pfn" $settings = Join-Path $state 'RoamingState\settings.json' # ── 5. First run: welcome page, then the defaults the app writes ──────── Log 'Launching TranslucentTB (packaged)' Start-Process explorer.exe "shell:AppsFolder\$pfn!TranslucentTB" $welcome = FindName 'Welcome to TranslucentTB!' 40 Start-Sleep 2 Shot '01-first-run-welcome' $facts.welcomePage = [bool]$welcome $cont = FindName 'Continue' 10 if ($cont) { $facts.clickedContinue = Invoke-El $cont; Log "Continue clicked: $($facts.clickedContinue)" } else { Log 'Continue button not found' } Start-Sleep 6 Shot '02-after-continue-default-look' if (Test-Path $settings) { Copy-Item $settings (Join-Path $out 'settings.written-by-app-on-first-run.json'); Log 'App wrote settings.json' } else { Log "settings.json not found at $settings" } $facts.settingsPathExists = Test-Path $settings $np = Start-Process notepad.exe -PassThru; Start-Sleep 3 $h = (Get-Process notepad | Where-Object MainWindowHandle -ne 0 | Select-Object -First 1).MainWindowHandle [N]::ShowWindow($h, 3) | Out-Null; Start-Sleep 3 Shot '03-defaults-maximized-window' Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -Force Key @(0x5B); Start-Sleep 3 Shot '04-defaults-start-open' Key @(0x1B); Start-Sleep 2 Get-ChildItem -Recurse $state -ErrorAction SilentlyContinue | Select-Object FullName, Length | Format-Table -AutoSize | Out-String -Width 300 | Set-Content (Join-Path $out 'package-folder.txt') function Write-Settings($json) { Set-Content -Path $settings -Value $json -Encoding utf8; Start-Sleep -Milliseconds 2500 } function Look($accent, $color, $extra = '') { return "{ `"accent`": `"$accent`", `"color`": `"$color`", `"show_peek`": false, `"show_line`": false $extra }" } function Cfg($desktop, $more = '') { return @" { "desktop_appearance": $desktop, "visible_window_appearance": { "enabled": false }, "maximized_window_appearance": { "enabled": false }, "start_opened_appearance": { "enabled": false }, "search_opened_appearance": { "enabled": false }, "task_view_opened_appearance": { "enabled": false }, "verbosity": "debug"$more } "@ } # ── 6. The five accents on the desktop state ──────────────────────────── $scen = @( @('10-desktop-clear-invisible', (Look 'clear' '#00000000')), @('11-desktop-clear-tinted', (Look 'clear' '#1D4ED880')), @('12-desktop-opaque', (Look 'opaque' '#1D4ED820')), @('13-desktop-blur-radius-30', (Look 'blur' '#00000010' ', "blur_radius": 30')), @('14-desktop-blur-radius-9', (Look 'blur' '#00000010' ', "blur_radius": 9')), @('15-desktop-acrylic', (Look 'acrylic' '#00000000')), @('16-desktop-acrylic-tinted', (Look 'acrylic' '#1D4ED860')), @('17-desktop-normal', (Look 'normal' '#00000000')), @('19-desktop-blur-radius-0', (Look 'blur' '#00000010' ', "blur_radius": 0')), @('19b-desktop-blur-radius-750', (Look 'blur' '#00000010' ', "blur_radius": 750')), @('18-desktop-clear-show-line', "{ `"accent`": `"clear`", `"color`": `"#00000000`", `"show_line`": true }") ) foreach ($s in $scen) { Write-Settings (Cfg $s[1]); Shot $s[0] } # ── 7. Dynamic modes with a real window ───────────────────────────────── Write-Settings (Cfg (Look 'clear' '#00000000') ",`n `"maximized_window_appearance`": { `"enabled`": true, `"accent`": `"opaque`", `"color`": `"#B42318FF`" },`n `"visible_window_appearance`": { `"enabled`": true, `"accent`": `"acrylic`", `"color`": `"#15734760`" }") $np = Start-Process notepad.exe -PassThru Start-Sleep 3 Shot '20-visible-window-acrylic-green' $h = (Get-Process -Id $np.Id).MainWindowHandle if ($h -eq [IntPtr]::Zero) { $h = (Get-Process notepad | Where-Object MainWindowHandle -ne 0 | Select-Object -First 1).MainWindowHandle } [N]::ShowWindow($h, 3) | Out-Null; Start-Sleep 3 Shot '21-maximized-window-opaque-red' [N]::ShowWindow($h, 6) | Out-Null; Start-Sleep 3 Shot '22-window-minimized-back-to-desktop' Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -Force # Rule: notepad.exe gets its own look when maximized. Write-Settings (Cfg (Look 'clear' '#00000000') ",`n `"maximized_window_appearance`": { `"enabled`": true, `"accent`": `"opaque`", `"color`": `"#B42318FF`", `"rules`": { `"process_name`": { `"Notepad.exe`": { `"accent`": `"opaque`", `"color`": `"#6D28D9FF`" } } } }") $np = Start-Process notepad.exe -PassThru; Start-Sleep 3 $h = (Get-Process notepad | Where-Object MainWindowHandle -ne 0 | Select-Object -First 1).MainWindowHandle [N]::ShowWindow($h, 3) | Out-Null; Start-Sleep 3 Shot '23-rule-process-name-notepad-purple' Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -Force # Start menu state. Write-Settings (Cfg (Look 'clear' '#00000000') ",`n `"start_opened_appearance`": { `"enabled`": true, `"accent`": `"opaque`", `"color`": `"#157347FF`" }") Key @(0x5B); Start-Sleep 3 Shot '24-start-opened-opaque-green' Key @(0x1B); Start-Sleep 2 # Reset-dynamic-state hotkey (Ctrl+Alt+Shift+F1) should not break anything. Key @(0x11, 0x12, 0x10, 0x70); Start-Sleep 2 Shot '25-after-reset-hotkey' # ── 8. Process cost ───────────────────────────────────────────────────── $p = Get-Process TranslucentTB -ErrorAction SilentlyContinue if ($p) { $samples = @() $c0 = $p.TotalProcessorTime.TotalMilliseconds for ($i = 0; $i -lt 10; $i++) { Start-Sleep 1; $p.Refresh(); $samples += [math]::Round($p.WorkingSet64 / 1MB, 1) } $c1 = $p.TotalProcessorTime.TotalMilliseconds $facts.process = [ordered]@{ workingSetMB = $samples; privateMB = [math]::Round($p.PrivateMemorySize64 / 1MB, 1); cpuMsOver10sIdle = [math]::Round($c1 - $c0, 1); threads = $p.Threads.Count } $facts.explorerModules = (Get-Process explorer | ForEach-Object { $_.Modules } | Where-Object { $_.ModuleName -match 'Explorer(TAP|Hooks)' } | ForEach-Object { "$($_.ModuleName) $($_.FileName)" }) -join '; ' } # ── 9. Invalid settings: the real error dialog ────────────────────────── Write-Settings (Cfg (Look 'Acrylic' '#00000000')) Start-Sleep 2 Shot '30-invalid-settings-error-dialog' $dlg = FindName 'OK' 5 if ($dlg) { Invoke-El $dlg | Out-Null } Start-Sleep 1 Write-Settings "{ `"desktop_appearance`": { `"accent`": `"clear`", `"color`": `"#00000000`" } `"oops`": 1 }" Start-Sleep 2 Shot '31-json-syntax-error-dialog' $dlg = FindName 'OK' 5 if ($dlg) { Invoke-El $dlg | Out-Null } Write-Settings (Cfg (Look 'acrylic' '#00000000')) # ── 10. Tray icon and menu ────────────────────────────────────────────── try { $chev = FindName 'Show Hidden Icons' 5 if ($chev) { Invoke-El $chev | Out-Null; Start-Sleep 2 } $icon = FindName 'TranslucentTB' 8 if ($icon) { $r = $icon.Current.BoundingRectangle Click ([int]($r.X + $r.Width / 2)) ([int]($r.Y + $r.Height / 2)) $true Start-Sleep 3 Shot '40-tray-menu' $desk = FindName 'Desktop' 3 if ($desk) { $dr = $desk.Current.BoundingRectangle; Click ([int]($dr.X + $dr.Width / 2)) ([int]($dr.Y + $dr.Height / 2)) $false; Start-Sleep 2; Shot '41-tray-menu-desktop-submenu' } Key @(0x1B); Start-Sleep 1 $adv = FindName 'Advanced' 3 if ($adv) { $ar = $adv.Current.BoundingRectangle; Click ([int]($ar.X + $ar.Width / 2)) ([int]($ar.Y + $ar.Height / 2)) $false; Start-Sleep 2; Shot '42-tray-menu-advanced' } $mx = FindName 'Maximized window' 3 Key @(0x1B); Start-Sleep 1 if ($mx) { $mr = $mx.Current.BoundingRectangle; Click ([int]($mr.X + $mr.Width / 2)) ([int]($mr.Y + $mr.Height / 2)) $false; Start-Sleep 2; Shot '43-tray-menu-maximized-submenu' } Key @(0x1B); Key @(0x1B) } else { Log 'Tray icon not found via UI Automation' } } catch { Log "tray menu step failed: $_" } # ── 10b. hide_tray: does starting the app again bring the icon back? ── Write-Settings ((Cfg (Look 'clear' '#00000000')) -replace '"verbosity": "debug"', '"verbosity": "debug", "hide_tray": true') Start-Sleep 2 $chev = FindName 'Show Hidden Icons' 5 if ($chev) { Invoke-El $chev | Out-Null; Start-Sleep 2 } Shot '44-hide-tray-true-overflow' $facts.hideTrayIconVisible = [bool](FindName 'TranslucentTB' 3) Key @(0x1B) Start-Process explorer.exe "shell:AppsFolder\$pfn!TranslucentTB"; Start-Sleep 5 $chev = FindName 'Show Hidden Icons' 5 if ($chev) { Invoke-El $chev | Out-Null; Start-Sleep 2 } Shot '45-hide-tray-after-second-launch' $facts.hideTrayIconAfterSecondLaunch = [bool](FindName 'TranslucentTB' 3) Key @(0x1B) Write-Settings (Cfg (Look 'clear' '#00000000')) Start-Sleep 2 $facts.hideTrayIconAfterSettingFalse = [bool](FindName 'TranslucentTB' 5) # ── 11. Logs, settings, second instance ───────────────────────────────── Start-Process explorer.exe "shell:AppsFolder\$pfn!TranslucentTB" Start-Sleep 4 Shot '50-second-instance-notification' Get-ChildItem (Join-Path $state 'TempState') -Filter *.log -ErrorAction SilentlyContinue | ForEach-Object { Copy-Item $_.FullName (Join-Path $out ("app-log-" + $_.Name)) } if (Test-Path $settings) { Copy-Item $settings (Join-Path $out 'settings.final.json') } Get-ChildItem (Join-Path $state 'TempState') -ErrorAction SilentlyContinue | Select-Object Name, Length | Format-Table | Out-String | Set-Content (Join-Path $out 'tempstate.txt') # ── 12. Portable build ────────────────────────────────────────────────── Get-Process TranslucentTB -ErrorAction SilentlyContinue | Stop-Process -Force Start-Sleep 3 Shot '60-after-killing-app-taskbar-restored' $pdir = Join-Path $env:TEMP 'ttb-portable' Expand-Archive (Join-Path $dl 'TranslucentTB-portable-x64.zip') $pdir -Force Get-ChildItem $pdir -Recurse | Select-Object FullName, Length | Format-Table -AutoSize | Out-String -Width 300 | Set-Content (Join-Path $out 'portable-files.txt') Start-Process (Join-Path $pdir 'TranslucentTB.exe') Start-Sleep 8 Shot '61-portable-first-run' $cont = FindName 'Continue' 10 if ($cont) { Invoke-El $cont | Out-Null; Start-Sleep 4 } $facts.portableSettingsNextToExe = Test-Path (Join-Path $pdir 'settings.json') $facts.portableTempLogs = (Get-ChildItem (Join-Path $env:TEMP 'TranslucentTB') -ErrorAction SilentlyContinue | ForEach-Object Name) -join ', ' Shot '62-portable-running' Get-Process TranslucentTB -ErrorAction SilentlyContinue | Stop-Process -Force # ── 13. Uninstall while the packaged app is running ───────────────────── if ($pkg) { Start-Process explorer.exe "shell:AppsFolder\$pfn!TranslucentTB"; Start-Sleep 8 Write-Settings (Cfg (Look 'opaque' '#B42318FF')); Start-Sleep 2 Shot '69-before-uninstall-red' $facts.ttbRunningBeforeUninstall = [bool](Get-Process TranslucentTB -ErrorAction SilentlyContinue) $before = (Get-Process explorer | Select-Object -First 1).Id Remove-AppxPackage $pkg.PackageFullName Start-Sleep 10 $after = (Get-Process explorer -ErrorAction SilentlyContinue | Select-Object -First 1).Id $facts.uninstall = [ordered]@{ packageGone = -not [bool](Get-AppxPackage *TranslucentTB*); packageFolderGone = -not (Test-Path $state); explorerPidBefore = $before; explorerPidAfter = $after } Shot '70-after-uninstall' $facts.uninstall.explorerStartTime = (Get-Process explorer -ErrorAction SilentlyContinue | Select-Object -First 1).StartTime.ToString('o') } $facts | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $out 'facts.json') Log 'done'