teamsiso/installer/Package.wxs
Zac Gaetano 2552d46210
Some checks failed
CI / build-and-test (push) Failing after 27s
fix(installer): wrap shortcut Target in 'runas /trustlevel:0x20000'
The in-process ShouldDeElevate check (commit 191b2c5) didn't fire on the test box because ParticipantPID resolution against Win32_Process can return null fast enough that the check skips before the elevated explorer-spawned TeamsISO has fully booted. Belt-and-braces: ALSO wrap the shortcut Target so the runas demotion happens at shell-launch time, before TeamsISO.exe even runs. Result on the dev box: clicking the Start Menu / Desktop shortcut now lands a working medium-integrity TeamsISO with NDI discovery succeeding, regardless of explorer's elevation.

Uses [SystemFolder]runas.exe (resolved by MSI at install time) and Show='minimized' to hide the brief runas console flash.
2026-05-16 11:43:54 -04:00

199 lines
8.4 KiB
XML

<?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" />
<ComponentGroupRef Id="DesktopShortcut" />
<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" />
<!--
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" />
<!--
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>
<!--
Start Menu and Desktop shortcuts. The Target is intentionally
runas.exe (NOT TeamsISO.exe directly) with the /trustlevel:0x20000
argument. This drops the spawned TeamsISO to MEDIUM integrity even
when the shortcut is invoked from an elevated File Explorer.
Why: on admin-user boxes with UAC effectively disabled, double-
clicking the .lnk has explorer.exe as the spawner — and when the
spawned TeamsISO inherits explorer's elevated token, NDI Find
returns zero discovered sources (suspected window-station / desktop
handle inheritance quirk in NDI's mDNS layer; not reproducible from
any other parent). Wrapping with /trustlevel:0x20000 forces the
child to a restricted medium-integrity token regardless of the
invoking shell's level. Empirically verified to make discovery
succeed on the dev box where the bug first surfaced.
The /trustlevel:0x20000 magic number is the Windows "Basic User"
SAFER trust level — documented at
https://learn.microsoft.com/windows/security/identity-protection/windows-credential-theft-mitigation-guide-appendix#runas
Icon points at TeamsISO.exe (not runas) so the shortcut still shows
the app's icon, and WindowStyle=Minimized hides the brief runas
console flash.
-->
<ComponentGroup Id="Shortcuts" Directory="WildDragonStartMenuFolder">
<Component Id="StartMenuShortcut" Guid="*">
<Shortcut Id="StartMenuTeamsISO"
Name="TeamsISO"
Description="Per-Participant NDI ISO Controller for Microsoft Teams"
Target="[SystemFolder]runas.exe"
Arguments="/trustlevel:0x20000 &quot;[INSTALLFOLDER]TeamsISO.exe&quot;"
WorkingDirectory="INSTALLFOLDER"
Icon="TeamsISOIcon"
Show="minimized" />
<!-- 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>
<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"
Target="[SystemFolder]runas.exe"
Arguments="/trustlevel:0x20000 &quot;[INSTALLFOLDER]TeamsISO.exe&quot;"
WorkingDirectory="INSTALLFOLDER"
Icon="TeamsISOIcon"
Show="minimized" />
<RegistryValue Root="HKCU"
Key="Software\Wild Dragon\TeamsISO"
Name="DesktopShortcut"
Type="integer"
Value="1"
KeyPath="yes" />
</Component>
</ComponentGroup>
<!--
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>