2026-05-08 00:16:26 -04:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<!--
|
|
|
|
|
TeamsISO — MSI installer (WiX v5)
|
|
|
|
|
|
|
|
|
|
Produces: TeamsISO-Setup-<Version>.msi (per-machine install).
|
|
|
|
|
|
|
|
|
|
Build:
|
|
|
|
|
dotnet publish src/TeamsISO.App/TeamsISO.App.csproj -c Release -r win-x64 (no self contained) -o publish/TeamsISO
|
|
|
|
|
dotnet build installer/TeamsISO.Installer.wixproj -c Release
|
|
|
|
|
|
|
|
|
|
Runtime expectations:
|
|
|
|
|
- .NET 8 Desktop runtime present on target (framework-dependent build)
|
|
|
|
|
- NDI 6 Runtime present — checked in CheckNdiRuntime; absence WARNS
|
|
|
|
|
but does not block install (operators can install NDI after the app)
|
|
|
|
|
-->
|
|
|
|
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
|
|
|
|
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
|
|
|
|
|
|
|
|
|
<Package Name="TeamsISO"
|
|
|
|
|
Manufacturer="Wild Dragon LLC"
|
|
|
|
|
Version="1.0.0.0"
|
|
|
|
|
UpgradeCode="9F4A8B2C-1D3E-4A5B-9C6D-8E7F0A1B2C3D"
|
|
|
|
|
Scope="perMachine"
|
|
|
|
|
Compressed="yes"
|
|
|
|
|
InstallerVersion="500">
|
|
|
|
|
|
|
|
|
|
<SummaryInformation Description="TeamsISO — Per-Participant NDI ISO Controller for Microsoft Teams"
|
|
|
|
|
Manufacturer="Wild Dragon LLC" />
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
MajorUpgrade: a newer install replaces an older one in-place.
|
|
|
|
|
Disallow downgrades; users should uninstall the newer first.
|
|
|
|
|
-->
|
|
|
|
|
<MajorUpgrade DowngradeErrorMessage="A newer version of TeamsISO is already installed. Uninstall it before installing this older version."
|
|
|
|
|
Schedule="afterInstallInitialize" />
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
Single MSI feature; users see only the install/uninstall screens.
|
|
|
|
|
-->
|
|
|
|
|
<Feature Id="Main" Title="TeamsISO" Level="1">
|
|
|
|
|
<ComponentGroupRef Id="ApplicationFiles" />
|
|
|
|
|
<ComponentGroupRef Id="Shortcuts" />
|
fix: cold-start discovery + installer shortcuts + single-instance hardening
Three independent fixes bundled because all were chasing the same operator
report: 'I just installed, launched from the shortcut, no participants.'
1) NdiDiscoveryService: poll immediately, then ramp from 200ms to the
configured interval over the first 3 seconds. PeriodicTimer.WaitForNext-
TickAsync waits the full interval before its first tick, so for a 500ms
discovery interval the operator stared at 'no ndi sources yet' for half
a second on every cold start. Force-poll up front (catches the runtime
cache), then run a fast inner loop for ~3s while mDNS replies trickle
in. Both loops share a try/finally so the NDI finder is always disposed.
2) MainViewModel.IsDiscovering: new boolean, true for 8s after engine start
AS LONG AS no participants have arrived. MainWindow.xaml swaps the
empty-state copy on this binding:
IsDiscovering=true -> 'scanning for ndi sources...' (cyan dot)
IsDiscovering=false -> 'no ndi sources visible -- is teams in a
meeting?' + Refresh CTA
The old copy ('no ndi sources yet -- open teams and start a meeting')
was being shown immediately at launch even when discovery just hadn't
run yet, making the app look broken.
3) App.xaml.cs: single-instance mutex moved from Local\ to Global\. On
admin-user boxes with UAC disabled, launches from different parents
(elevated File Explorer, non-elevated shell, etc.) can land in slightly
different security contexts and a Local\ name can be invisible to the
sibling. Global\ namespace closes that hole — both processes see the
same mutex regardless of integrity. Belt-and-braces against future
dual-instance file/port contention.
4) installer/Package.wxs: add a Desktop shortcut component (per-machine
feature, HKCU keypath per ICE38/ICE43). Operators who can't find the
Start Menu entry get the Desktop icon. Both shortcuts target the
installed exe, NOT a stale path under publish/.
2026-05-16 11:23:19 -04:00
|
|
|
<ComponentGroupRef Id="DesktopShortcut" />
|
2026-05-08 00:16:26 -04:00
|
|
|
<ComponentGroupRef Id="ArpEntry" />
|
|
|
|
|
</Feature>
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
Friendly install UI. WixToolset.UI.wixext provides several flavors;
|
|
|
|
|
WixUI_InstallDir lets the user pick the directory.
|
|
|
|
|
-->
|
|
|
|
|
<ui:WixUI Id="WixUI_InstallDir" />
|
|
|
|
|
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
ARP icon + about-box link.
|
|
|
|
|
-->
|
|
|
|
|
<Property Id="ARPHELPLINK" Value="https://wilddragon.net" />
|
|
|
|
|
<Property Id="ARPURLINFOABOUT" Value="https://wilddragon.net" />
|
|
|
|
|
<!-- ARPNOMODIFY is set by WixUI_InstallDir; don't redeclare. -->
|
|
|
|
|
<Property Id="ARPNOREPAIR" Value="1" />
|
|
|
|
|
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
<!--
|
|
|
|
|
ARP icon — references the same .ico the WPF host uses. WiX requires the
|
|
|
|
|
icon resource to live next to the wxs OR be reachable at build time;
|
|
|
|
|
we point at the published copy under src/TeamsISO.App/Assets so the icon
|
|
|
|
|
embedded in the MSI matches the icon in the running exe.
|
|
|
|
|
-->
|
|
|
|
|
<Icon Id="TeamsISOIcon" SourceFile="$(var.AssetsDir)teamsiso.ico" />
|
|
|
|
|
<Property Id="ARPPRODUCTICON" Value="TeamsISOIcon" />
|
|
|
|
|
|
2026-05-08 00:16:26 -04:00
|
|
|
<!--
|
|
|
|
|
NDI Runtime detection. We check for NDI_RUNTIME_DIR_V6 in the system
|
|
|
|
|
environment block. Missing → warn during install, don't block. The
|
|
|
|
|
engine surfaces a clear MessageBox with an install-NDI link at first
|
|
|
|
|
launch if the runtime really isn't there.
|
|
|
|
|
-->
|
|
|
|
|
<Property Id="NDIRUNTIMEDIR" Value="0">
|
|
|
|
|
<RegistrySearch Id="NdiRuntimeDirV6Search"
|
|
|
|
|
Root="HKLM"
|
|
|
|
|
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
|
|
|
|
|
Name="NDI_RUNTIME_DIR_V6"
|
|
|
|
|
Type="raw" />
|
|
|
|
|
</Property>
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
NDI runtime detection is surfaced at first app launch (App.xaml.cs pops a
|
|
|
|
|
MessageBox with an install link). We deliberately don't block install on
|
|
|
|
|
a missing runtime so admins can stage the app before NDI is rolled out.
|
|
|
|
|
VBScript-based install-time prompts are deprecated in WiX v5 / Windows
|
|
|
|
|
and rewriting in C++ is overkill for a soft warning.
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
Install layout under Program Files\Wild Dragon\TeamsISO.
|
|
|
|
|
-->
|
|
|
|
|
<StandardDirectory Id="ProgramFiles64Folder">
|
|
|
|
|
<Directory Id="ManufacturerFolder" Name="Wild Dragon">
|
|
|
|
|
<Directory Id="INSTALLFOLDER" Name="TeamsISO" />
|
|
|
|
|
</Directory>
|
|
|
|
|
</StandardDirectory>
|
|
|
|
|
|
|
|
|
|
<StandardDirectory Id="ProgramMenuFolder">
|
|
|
|
|
<Directory Id="WildDragonStartMenuFolder" Name="Wild Dragon" />
|
|
|
|
|
</StandardDirectory>
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
Files: harvested from the publish output dir at build time.
|
|
|
|
|
WiX v5 understands <Files Include="..."> with glob patterns and
|
|
|
|
|
synthesizes one Component per file with stable GUIDs.
|
|
|
|
|
-->
|
|
|
|
|
<ComponentGroup Id="ApplicationFiles" Directory="INSTALLFOLDER">
|
|
|
|
|
<Files Include="$(var.PublishDir)**" />
|
|
|
|
|
</ComponentGroup>
|
|
|
|
|
|
|
|
|
|
<!--
|
fix(installer+wpf): REVERT runas /trustlevel demotion (it was the bug, not the fix)
Massive misdiagnosis correction. The 2025-05-16 effort to 'fix elevation' has been actively breaking every Start Menu / Desktop shortcut launch since rc7. Empirical retrace:
- Elevated PowerShell -> Process.Start(exe) -> elevated TeamsISO -> WORKS
- Elevated PowerShell with --keep-elevation -> elevated TeamsISO -> WORKS (vm.Participants.Count=2)
- Non-elevated PS Process.Start(exe) -> medium TeamsISO -> WORKS
- ANY launch through runas /trustlevel:0x20000 -> SAFER-restricted TeamsISO -> BROKEN (window appears, zero managed code runs past BAML parse, no logs, no port binds)
The SAFER-restricted token that runas /trustlevel produces breaks .NET 8 WPF apphost in a way that leaves the process apparently alive (with the MainWindow.xaml rendering the empty state from default property values) but executing zero managed code. So my StartupTrace, Serilog file sink, and ControlSurface bind all silently failed for every shortcut launch. Looked exactly like 'cold-start NDI Find stuck at zero' from the outside but had nothing to do with NDI.
Revert:
- installer/Package.wxs: shortcuts target the .exe directly, no runas wrapper
- App.xaml.cs: removed ShouldDeElevate, TryDeElevateAndExit, RelaunchEnvVar, --keep-elevation/--relaunched handling. The check is gone, not just disabled, so future-me can't bring it back without re-discovering the same bug.
Kept:
- StartupTrace (still useful for any future startup mystery)
- Self-healing NDI Find rebuild (c30a616) - still valuable for legitimate stuck-finder cases
- System.Management PackageReference - TryGetParentProcessName still used in StartupTrace
Verified post-revert: Start Menu shortcut click -> PID 43060 -> full trace -> REST 2 participants. 252/252 tests still passing.
2026-05-16 16:27:23 -04:00
|
|
|
Start Menu and Desktop shortcuts — direct .exe targets.
|
|
|
|
|
|
|
|
|
|
History note: an earlier revision wrapped the Target in
|
|
|
|
|
runas.exe /trustlevel:0x20000 to drop the spawned TeamsISO to
|
|
|
|
|
medium integrity, on the theory that elevated TeamsISO couldn't
|
|
|
|
|
discover NDI sources. THAT THEORY WAS WRONG. Verified empirically
|
|
|
|
|
2026-05-16: elevated TeamsISO discovers NDI sources fine
|
|
|
|
|
(vm.Participants.Count=2 at +5s with the keep-elevation flag
|
|
|
|
|
forcing OnStartup past the de-elevation check). The actual bug was the
|
|
|
|
|
SAFER-restricted token produced by runas /trustlevel (the demotion) breaks
|
|
|
|
|
.NET 8 WPF apphost startup in a way that the process appears alive
|
|
|
|
|
with a window but executes zero managed code past the very first
|
|
|
|
|
BAML-parse for MainWindow.xaml. No logs, no port binds, no
|
|
|
|
|
controller subscription. The runas wrapper was actively causing
|
|
|
|
|
every "shortcut launch shows no participants" report.
|
|
|
|
|
|
|
|
|
|
Direct .exe target. The in-app `ShouldDeElevate` check (App.xaml.cs)
|
|
|
|
|
has also been removed for the same reason — letting TeamsISO run
|
|
|
|
|
elevated is strictly better than re-spawning it through runas.
|
2026-05-08 00:16:26 -04:00
|
|
|
-->
|
|
|
|
|
<ComponentGroup Id="Shortcuts" Directory="WildDragonStartMenuFolder">
|
|
|
|
|
<Component Id="StartMenuShortcut" Guid="*">
|
|
|
|
|
<Shortcut Id="StartMenuTeamsISO"
|
|
|
|
|
Name="TeamsISO"
|
|
|
|
|
Description="Per-Participant NDI ISO Controller for Microsoft Teams"
|
fix(installer+wpf): REVERT runas /trustlevel demotion (it was the bug, not the fix)
Massive misdiagnosis correction. The 2025-05-16 effort to 'fix elevation' has been actively breaking every Start Menu / Desktop shortcut launch since rc7. Empirical retrace:
- Elevated PowerShell -> Process.Start(exe) -> elevated TeamsISO -> WORKS
- Elevated PowerShell with --keep-elevation -> elevated TeamsISO -> WORKS (vm.Participants.Count=2)
- Non-elevated PS Process.Start(exe) -> medium TeamsISO -> WORKS
- ANY launch through runas /trustlevel:0x20000 -> SAFER-restricted TeamsISO -> BROKEN (window appears, zero managed code runs past BAML parse, no logs, no port binds)
The SAFER-restricted token that runas /trustlevel produces breaks .NET 8 WPF apphost in a way that leaves the process apparently alive (with the MainWindow.xaml rendering the empty state from default property values) but executing zero managed code. So my StartupTrace, Serilog file sink, and ControlSurface bind all silently failed for every shortcut launch. Looked exactly like 'cold-start NDI Find stuck at zero' from the outside but had nothing to do with NDI.
Revert:
- installer/Package.wxs: shortcuts target the .exe directly, no runas wrapper
- App.xaml.cs: removed ShouldDeElevate, TryDeElevateAndExit, RelaunchEnvVar, --keep-elevation/--relaunched handling. The check is gone, not just disabled, so future-me can't bring it back without re-discovering the same bug.
Kept:
- StartupTrace (still useful for any future startup mystery)
- Self-healing NDI Find rebuild (c30a616) - still valuable for legitimate stuck-finder cases
- System.Management PackageReference - TryGetParentProcessName still used in StartupTrace
Verified post-revert: Start Menu shortcut click -> PID 43060 -> full trace -> REST 2 participants. 252/252 tests still passing.
2026-05-16 16:27:23 -04:00
|
|
|
Target="[INSTALLFOLDER]TeamsISO.exe"
|
2026-05-16 11:43:54 -04:00
|
|
|
WorkingDirectory="INSTALLFOLDER"
|
fix(installer+wpf): REVERT runas /trustlevel demotion (it was the bug, not the fix)
Massive misdiagnosis correction. The 2025-05-16 effort to 'fix elevation' has been actively breaking every Start Menu / Desktop shortcut launch since rc7. Empirical retrace:
- Elevated PowerShell -> Process.Start(exe) -> elevated TeamsISO -> WORKS
- Elevated PowerShell with --keep-elevation -> elevated TeamsISO -> WORKS (vm.Participants.Count=2)
- Non-elevated PS Process.Start(exe) -> medium TeamsISO -> WORKS
- ANY launch through runas /trustlevel:0x20000 -> SAFER-restricted TeamsISO -> BROKEN (window appears, zero managed code runs past BAML parse, no logs, no port binds)
The SAFER-restricted token that runas /trustlevel produces breaks .NET 8 WPF apphost in a way that leaves the process apparently alive (with the MainWindow.xaml rendering the empty state from default property values) but executing zero managed code. So my StartupTrace, Serilog file sink, and ControlSurface bind all silently failed for every shortcut launch. Looked exactly like 'cold-start NDI Find stuck at zero' from the outside but had nothing to do with NDI.
Revert:
- installer/Package.wxs: shortcuts target the .exe directly, no runas wrapper
- App.xaml.cs: removed ShouldDeElevate, TryDeElevateAndExit, RelaunchEnvVar, --keep-elevation/--relaunched handling. The check is gone, not just disabled, so future-me can't bring it back without re-discovering the same bug.
Kept:
- StartupTrace (still useful for any future startup mystery)
- Self-healing NDI Find rebuild (c30a616) - still valuable for legitimate stuck-finder cases
- System.Management PackageReference - TryGetParentProcessName still used in StartupTrace
Verified post-revert: Start Menu shortcut click -> PID 43060 -> full trace -> REST 2 participants. 252/252 tests still passing.
2026-05-16 16:27:23 -04:00
|
|
|
Icon="TeamsISOIcon" />
|
2026-05-08 00:16:26 -04:00
|
|
|
<!-- Required by ICE64: Start Menu folder must be cleaned on uninstall. -->
|
|
|
|
|
<RemoveFolder Id="RemoveWildDragonStartMenuFolder"
|
|
|
|
|
Directory="WildDragonStartMenuFolder"
|
|
|
|
|
On="uninstall" />
|
|
|
|
|
<RegistryValue Root="HKCU"
|
|
|
|
|
Key="Software\Wild Dragon\TeamsISO"
|
|
|
|
|
Name="StartMenuShortcut"
|
|
|
|
|
Type="integer"
|
|
|
|
|
Value="1"
|
|
|
|
|
KeyPath="yes" />
|
|
|
|
|
</Component>
|
|
|
|
|
</ComponentGroup>
|
|
|
|
|
|
fix: cold-start discovery + installer shortcuts + single-instance hardening
Three independent fixes bundled because all were chasing the same operator
report: 'I just installed, launched from the shortcut, no participants.'
1) NdiDiscoveryService: poll immediately, then ramp from 200ms to the
configured interval over the first 3 seconds. PeriodicTimer.WaitForNext-
TickAsync waits the full interval before its first tick, so for a 500ms
discovery interval the operator stared at 'no ndi sources yet' for half
a second on every cold start. Force-poll up front (catches the runtime
cache), then run a fast inner loop for ~3s while mDNS replies trickle
in. Both loops share a try/finally so the NDI finder is always disposed.
2) MainViewModel.IsDiscovering: new boolean, true for 8s after engine start
AS LONG AS no participants have arrived. MainWindow.xaml swaps the
empty-state copy on this binding:
IsDiscovering=true -> 'scanning for ndi sources...' (cyan dot)
IsDiscovering=false -> 'no ndi sources visible -- is teams in a
meeting?' + Refresh CTA
The old copy ('no ndi sources yet -- open teams and start a meeting')
was being shown immediately at launch even when discovery just hadn't
run yet, making the app look broken.
3) App.xaml.cs: single-instance mutex moved from Local\ to Global\. On
admin-user boxes with UAC disabled, launches from different parents
(elevated File Explorer, non-elevated shell, etc.) can land in slightly
different security contexts and a Local\ name can be invisible to the
sibling. Global\ namespace closes that hole — both processes see the
same mutex regardless of integrity. Belt-and-braces against future
dual-instance file/port contention.
4) installer/Package.wxs: add a Desktop shortcut component (per-machine
feature, HKCU keypath per ICE38/ICE43). Operators who can't find the
Start Menu entry get the Desktop icon. Both shortcuts target the
installed exe, NOT a stale path under publish/.
2026-05-16 11:23:19 -04:00
|
|
|
<StandardDirectory Id="DesktopFolder" />
|
|
|
|
|
<ComponentGroup Id="DesktopShortcut" Directory="DesktopFolder">
|
|
|
|
|
<Component Id="DesktopShortcutComponent" Guid="*">
|
|
|
|
|
<Shortcut Id="DesktopTeamsISO"
|
|
|
|
|
Name="TeamsISO"
|
|
|
|
|
Description="Per-Participant NDI ISO Controller for Microsoft Teams"
|
fix(installer+wpf): REVERT runas /trustlevel demotion (it was the bug, not the fix)
Massive misdiagnosis correction. The 2025-05-16 effort to 'fix elevation' has been actively breaking every Start Menu / Desktop shortcut launch since rc7. Empirical retrace:
- Elevated PowerShell -> Process.Start(exe) -> elevated TeamsISO -> WORKS
- Elevated PowerShell with --keep-elevation -> elevated TeamsISO -> WORKS (vm.Participants.Count=2)
- Non-elevated PS Process.Start(exe) -> medium TeamsISO -> WORKS
- ANY launch through runas /trustlevel:0x20000 -> SAFER-restricted TeamsISO -> BROKEN (window appears, zero managed code runs past BAML parse, no logs, no port binds)
The SAFER-restricted token that runas /trustlevel produces breaks .NET 8 WPF apphost in a way that leaves the process apparently alive (with the MainWindow.xaml rendering the empty state from default property values) but executing zero managed code. So my StartupTrace, Serilog file sink, and ControlSurface bind all silently failed for every shortcut launch. Looked exactly like 'cold-start NDI Find stuck at zero' from the outside but had nothing to do with NDI.
Revert:
- installer/Package.wxs: shortcuts target the .exe directly, no runas wrapper
- App.xaml.cs: removed ShouldDeElevate, TryDeElevateAndExit, RelaunchEnvVar, --keep-elevation/--relaunched handling. The check is gone, not just disabled, so future-me can't bring it back without re-discovering the same bug.
Kept:
- StartupTrace (still useful for any future startup mystery)
- Self-healing NDI Find rebuild (c30a616) - still valuable for legitimate stuck-finder cases
- System.Management PackageReference - TryGetParentProcessName still used in StartupTrace
Verified post-revert: Start Menu shortcut click -> PID 43060 -> full trace -> REST 2 participants. 252/252 tests still passing.
2026-05-16 16:27:23 -04:00
|
|
|
Target="[INSTALLFOLDER]TeamsISO.exe"
|
fix: cold-start discovery + installer shortcuts + single-instance hardening
Three independent fixes bundled because all were chasing the same operator
report: 'I just installed, launched from the shortcut, no participants.'
1) NdiDiscoveryService: poll immediately, then ramp from 200ms to the
configured interval over the first 3 seconds. PeriodicTimer.WaitForNext-
TickAsync waits the full interval before its first tick, so for a 500ms
discovery interval the operator stared at 'no ndi sources yet' for half
a second on every cold start. Force-poll up front (catches the runtime
cache), then run a fast inner loop for ~3s while mDNS replies trickle
in. Both loops share a try/finally so the NDI finder is always disposed.
2) MainViewModel.IsDiscovering: new boolean, true for 8s after engine start
AS LONG AS no participants have arrived. MainWindow.xaml swaps the
empty-state copy on this binding:
IsDiscovering=true -> 'scanning for ndi sources...' (cyan dot)
IsDiscovering=false -> 'no ndi sources visible -- is teams in a
meeting?' + Refresh CTA
The old copy ('no ndi sources yet -- open teams and start a meeting')
was being shown immediately at launch even when discovery just hadn't
run yet, making the app look broken.
3) App.xaml.cs: single-instance mutex moved from Local\ to Global\. On
admin-user boxes with UAC disabled, launches from different parents
(elevated File Explorer, non-elevated shell, etc.) can land in slightly
different security contexts and a Local\ name can be invisible to the
sibling. Global\ namespace closes that hole — both processes see the
same mutex regardless of integrity. Belt-and-braces against future
dual-instance file/port contention.
4) installer/Package.wxs: add a Desktop shortcut component (per-machine
feature, HKCU keypath per ICE38/ICE43). Operators who can't find the
Start Menu entry get the Desktop icon. Both shortcuts target the
installed exe, NOT a stale path under publish/.
2026-05-16 11:23:19 -04:00
|
|
|
WorkingDirectory="INSTALLFOLDER"
|
fix(installer+wpf): REVERT runas /trustlevel demotion (it was the bug, not the fix)
Massive misdiagnosis correction. The 2025-05-16 effort to 'fix elevation' has been actively breaking every Start Menu / Desktop shortcut launch since rc7. Empirical retrace:
- Elevated PowerShell -> Process.Start(exe) -> elevated TeamsISO -> WORKS
- Elevated PowerShell with --keep-elevation -> elevated TeamsISO -> WORKS (vm.Participants.Count=2)
- Non-elevated PS Process.Start(exe) -> medium TeamsISO -> WORKS
- ANY launch through runas /trustlevel:0x20000 -> SAFER-restricted TeamsISO -> BROKEN (window appears, zero managed code runs past BAML parse, no logs, no port binds)
The SAFER-restricted token that runas /trustlevel produces breaks .NET 8 WPF apphost in a way that leaves the process apparently alive (with the MainWindow.xaml rendering the empty state from default property values) but executing zero managed code. So my StartupTrace, Serilog file sink, and ControlSurface bind all silently failed for every shortcut launch. Looked exactly like 'cold-start NDI Find stuck at zero' from the outside but had nothing to do with NDI.
Revert:
- installer/Package.wxs: shortcuts target the .exe directly, no runas wrapper
- App.xaml.cs: removed ShouldDeElevate, TryDeElevateAndExit, RelaunchEnvVar, --keep-elevation/--relaunched handling. The check is gone, not just disabled, so future-me can't bring it back without re-discovering the same bug.
Kept:
- StartupTrace (still useful for any future startup mystery)
- Self-healing NDI Find rebuild (c30a616) - still valuable for legitimate stuck-finder cases
- System.Management PackageReference - TryGetParentProcessName still used in StartupTrace
Verified post-revert: Start Menu shortcut click -> PID 43060 -> full trace -> REST 2 participants. 252/252 tests still passing.
2026-05-16 16:27:23 -04:00
|
|
|
Icon="TeamsISOIcon" />
|
fix: cold-start discovery + installer shortcuts + single-instance hardening
Three independent fixes bundled because all were chasing the same operator
report: 'I just installed, launched from the shortcut, no participants.'
1) NdiDiscoveryService: poll immediately, then ramp from 200ms to the
configured interval over the first 3 seconds. PeriodicTimer.WaitForNext-
TickAsync waits the full interval before its first tick, so for a 500ms
discovery interval the operator stared at 'no ndi sources yet' for half
a second on every cold start. Force-poll up front (catches the runtime
cache), then run a fast inner loop for ~3s while mDNS replies trickle
in. Both loops share a try/finally so the NDI finder is always disposed.
2) MainViewModel.IsDiscovering: new boolean, true for 8s after engine start
AS LONG AS no participants have arrived. MainWindow.xaml swaps the
empty-state copy on this binding:
IsDiscovering=true -> 'scanning for ndi sources...' (cyan dot)
IsDiscovering=false -> 'no ndi sources visible -- is teams in a
meeting?' + Refresh CTA
The old copy ('no ndi sources yet -- open teams and start a meeting')
was being shown immediately at launch even when discovery just hadn't
run yet, making the app look broken.
3) App.xaml.cs: single-instance mutex moved from Local\ to Global\. On
admin-user boxes with UAC disabled, launches from different parents
(elevated File Explorer, non-elevated shell, etc.) can land in slightly
different security contexts and a Local\ name can be invisible to the
sibling. Global\ namespace closes that hole — both processes see the
same mutex regardless of integrity. Belt-and-braces against future
dual-instance file/port contention.
4) installer/Package.wxs: add a Desktop shortcut component (per-machine
feature, HKCU keypath per ICE38/ICE43). Operators who can't find the
Start Menu entry get the Desktop icon. Both shortcuts target the
installed exe, NOT a stale path under publish/.
2026-05-16 11:23:19 -04:00
|
|
|
<RegistryValue Root="HKCU"
|
|
|
|
|
Key="Software\Wild Dragon\TeamsISO"
|
|
|
|
|
Name="DesktopShortcut"
|
|
|
|
|
Type="integer"
|
|
|
|
|
Value="1"
|
|
|
|
|
KeyPath="yes" />
|
|
|
|
|
</Component>
|
|
|
|
|
</ComponentGroup>
|
|
|
|
|
|
2026-05-08 00:16:26 -04:00
|
|
|
<!--
|
|
|
|
|
ARP icon registry entry. Optional — the MSI auto-fills most ARP
|
|
|
|
|
fields from the Package element. We only need to point at the
|
|
|
|
|
executable for the ARP icon.
|
|
|
|
|
-->
|
|
|
|
|
<ComponentGroup Id="ArpEntry" Directory="INSTALLFOLDER">
|
|
|
|
|
<Component Id="ArpIconRegistry" Guid="*">
|
|
|
|
|
<RegistryValue Root="HKLM"
|
|
|
|
|
Key="Software\Wild Dragon\TeamsISO"
|
|
|
|
|
Name="InstallPath"
|
|
|
|
|
Type="string"
|
|
|
|
|
Value="[INSTALLFOLDER]"
|
|
|
|
|
KeyPath="yes" />
|
|
|
|
|
</Component>
|
|
|
|
|
</ComponentGroup>
|
|
|
|
|
|
|
|
|
|
</Package>
|
|
|
|
|
</Wix>
|