teamsiso/NEXT_STEPS.md
2026-05-16 11:39:31 -04:00

157 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Where we left off — explorer-spawn de-elevation shipped (2026-05-16)
## The actual root cause (finally)
When TeamsISO is spawned by an **elevated File Explorer**, NDI Find returns
zero discovered sources even though Teams is broadcasting. The same exe
spawned from any other parent (PowerShell, cmd, runas, another TeamsISO,
etc.) discovers sources fine — even when that parent is itself elevated.
I reproduced this multiple times with the same install:
| Launch parent | Integrity | Result |
|---|---|---|
| non-elevated PowerShell | medium | OK — 2 participants |
| elevated PowerShell (via `-Verb RunAs`) | high | OK — 2 participants |
| `runas /trustlevel:0x20000` | medium | OK — 2 participants |
| elevated Explorer (operator click) | high | **EMPTY** — 0 participants |
Same exe, same install path, same user, same NDI runtime, same Teams
meeting. The only differentiator is `parent.ImageName == "explorer.exe"`
combined with elevation. The suspicion is a window-station / desktop-handle
inheritance quirk in NDI's mDNS implementation — explorer spawns with
shell-specific STARTUPINFOEX attributes that NDI Find apparently can't
work through. Not fixable from inside TeamsISO at the runtime layer.
This is the actual reason every "I clicked the shortcut and saw no
participants" report happened. The earlier "cold-start polling" and
"single-instance integrity isolation" theories were both wrong — those
fixes were independently good but not the cause.
## The fix (`191b2c5`)
`App.OnStartup` now runs an elevation check before any other startup work:
1. If `--relaunched` is in args, skip the check (loop guard).
2. If we're not in the Administrators role, skip.
3. If our parent process is NOT `explorer.exe`, skip.
4. Otherwise — re-spawn ourselves via
`runas.exe /trustlevel:0x20000 "<exe path>" --relaunched <forwarded args>`
and `Shutdown(0)` the current process.
`runas /trustlevel:0x20000` requests a **medium-integrity restricted token**
even when the caller is elevated. The new child appears with `runas.exe`
as its parent (NOT explorer.exe), at medium integrity, with the
`--relaunched` flag so the de-elevation check no-ops on the second pass.
The check uses `System.Management.ManagementObjectSearcher` against
`Win32_Process` to find the parent PID — added as a `PackageReference` in
the csproj.
## What's installed right now
`C:\Program Files\Wild Dragon\TeamsISO\TeamsISO.exe`**0.9.0-rc6** with
the de-elevation logic, timestamp `2026-05-16 11:36:28`. Shortcuts present
at `C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Wild Dragon\TeamsISO.lnk`
and `C:\Users\Public\Desktop\TeamsISO.lnk`, both pointing at the installed
exe.
Three stale install records were left over from previous rc1rc5 attempts
(all `DisplayName=TeamsISO`, three different ProductCodes). All three were
uninstalled before -rc6 went on cleanly. Only one TeamsISO ARP entry now.
## How to verify
Double-click `C:\Program Files\Wild Dragon\TeamsISO\TeamsISO.exe` or click
the Start Menu / Desktop shortcut from File Explorer. The expected sequence:
1. Brief flash of a window that immediately closes (the elevated initial
process detecting explorer-spawn and re-launching).
2. A second TeamsISO window appears, parented under `runas.exe`, at
medium integrity.
3. Participants discover within ~3 seconds.
The log file at `C:\Users\zacga\AppData\Local\TeamsISO\Logs\teamsiso<date>.log`
will show one "TeamsISO.App starting up" line — NOT two — because the
elevated first process exits before initializing the logger.
If discovery STILL stays empty, options:
- The `runas /trustlevel` spawn may have failed silently. Diagnostics
aren't great here because the logger isn't up yet at the de-elevation
point. We could log to a fallback raw text file.
- The `Secondary Logon` Windows service might be disabled (it's required
for runas). `Get-Service seclogon | Format-List` to check; should be
Running.
## All commits on origin (newest first)
```
191b2c5 fix(wpf): de-elevate when spawned by elevated explorer (NDI mDNS isolation)
e01fa36 docs(next-steps): cold-start launch fix verified — 3 launch paths green
09e5b59 fix: cold-start discovery + installer shortcuts + single-instance hardening
f47edfb ISO toggle: widen column 110->124, tighten padding so 'Enable' fits
47914fc ISO toggle: square corners to match the rest of the button family
dba7dcc gear icon: swap Path glyph for U+2699 + bump column to 56px
6c9bee7 fix(wpf): catch participant-left race in ToggleIsoAsync, toast instead of crash
84861da test: integration — App+MainWindow STA smoke, control-surface live VM, theme XAML load
6505a3c test: services — NotesService, UpdateChecker, PresetApplier, OscBridge, IsoController
d91f953 test: ControlSurfaceServer route table smoke coverage
fbcc562 test: ThemeManager + CommandPaletteViewModel.Matches coverage
e96a30b chore: trim stale batch-commit script + drop SmokeTest placeholder
1f07992 refactor(services): extract TeamsEmbedHost from TeamsLauncher
2640739 refactor(control-surface): split server into endpoint partials
e67c02c refactor(app): split App.xaml.cs into themed partial files
d02a2c0 refactor(viewmodels): split MainViewModel into themed partial classes
33fca8e polish(mainwindow): empty state, table widths, strings, theme tooltip
3739002 chore(docs): reconcile to WPF-only after WinUI 3 was abandoned
5a43c9c feat: per-ISO framerate/resolution/aspect/audio overrides + thumbnail BMP
```
246/246 tests passing on the merged main.
## Pre-1.0 cut still gated on
1. Code-signing the MSI. `SIGN_CERT_PFX_BASE64` + `SIGN_CERT_PASSWORD`
need to go into Forgejo Actions Secrets for `release.yml` to start
producing signed MSIs. Without that, downstream operators get the
"Windows protected your PC" SmartScreen warning.
2. Real-meeting smoke pass on a non-dev host with a live NDI runtime.
## Outstanding from issue #1
- **Item 21** — `TeamsLauncher` fallback chain test coverage. Still
needs the `IProcessLauncher` seam refactor before the URI handler →
AppX → process-exe order can be unit-pinned. Half-day of work.
## Build / install cheatsheet
```powershell
cd "C:\Users\zacga\Documents\Claude\Projects\Teams ISO"
# Build + test
dotnet build TeamsISO.sln -c Release # 0 warnings / 0 errors
dotnet test TeamsISO.sln -c Release --no-build # 246/246 passing
# Publish + MSI
$v = "0.9.0-rcN"
dotnet publish src/TeamsISO.App/TeamsISO.App.csproj `
-c Release -r win-x64 --self-contained false `
-o publish/TeamsISO /p:Version=$v
dotnet build installer/TeamsISO.Installer.wixproj -c Release /p:Version=$v
# Install (uninstall first if upgrading from same Version="1.0.0.0"!)
Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' |
Where-Object DisplayName -like '*TeamsISO*' |
ForEach-Object {
Start-Process msiexec.exe -Verb RunAs -Wait -ArgumentList "/x $($_.PSChildName) /qn /norestart"
}
Start-Process msiexec.exe -Verb RunAs -Wait -ArgumentList `
'/i', '"installer\bin\x64\Release\TeamsISO-Setup-' + $v + '.msi"', '/qn', '/norestart'
```
## Rollback
If the de-elevation logic breaks on a different machine config, revert
just commit `191b2c5` — the earlier `e01fa36` build (with cold-start
polling + Global mutex + dual shortcuts but no de-elevation) is the
safe-fallback baseline.