Repository navigation
Replace the bundled NirSoft tools with our own code (unblocks code signing) #91
Description
Why
Console Mode bundles two NirSoft tools inside the executable: MultiMonitorTool (displays) and SoundVolumeView (audio). They are free but closed source, and that blocks two things:
- Code signing. The free SignPath Foundation program requires no proprietary code in the signed package. Unsigned releases show Windows SmartScreen's "unknown publisher" warning, which likely scares off more users than any bug. The repo side is ready (
docs/CODE_SIGNING.mdand the gated steps inrelease.yml, chore: prepare SignPath code signing (policy page, gated release steps) #90); this is the remaining blocker. - Antivirus false positives. The README already warns that antivirus may flag the bundled tools.
Replacing them with our own code (Win32 / Core Audio via P/Invoke) removes both problems and the dependency on nirsoft.net at build time.
Scope
Every call goes through two wrappers, so the migration can happen behind them:
Displays: MonitorService.InvokeMmt (~20 calls)
| MultiMonitorTool command | Used for | Native replacement |
|---|---|---|
/scomma + /HideInactiveMonitors 0 |
List monitors (active and inactive) | QueryDisplayConfig (CCD) + EnumDisplayDevices |
/SaveConfig, /LoadConfig |
Back up and restore the desk layout | Store CCD paths/modes ourselves; SetDisplayConfig to restore |
/enable, /disable, /SetMonitors |
Turn displays on/off, set positions | SetDisplayConfig (CCD topology) |
/SetPrimary |
Make the TV primary | ChangeDisplaySettingsEx with CDS_SET_PRIMARY + repositioning |
/TurnOn, /TurnOff |
Power displays (DDC/CI) | SetVCPFeature (dxva2 Monitor Configuration API) |
/MoveWindow |
Move a window to a monitor | SetWindowPos |
Native/CcdHelper.cs already talks to CCD (ExtendAll), so there is a starting point.
Audio: AudioService.InvokeSvv (~10 calls)
| SoundVolumeView command | Used for | Native replacement |
|---|---|---|
/scomma + /ShowDisabledDevices /ShowUnpluggedDevices |
List outputs | IMMDeviceEnumerator (Core Audio) |
/SetDefault … all |
Switch the default output (all roles) | IPolicyConfig (undocumented COM, the same one NirSoft, EarTrumpet and AudioSwitcher use) |
/Enable |
Enable a disabled output | IPolicyConfig::SetEndpointVisibility |
/GetPercent, /SetVolume, /Mute, /Unmute |
Session menu volume | IAudioEndpointVolume |
Plan
- Audio first. Smaller, well-known APIs, easy to verify by ear. One PR.
- Displays in steps: listing → enable/disable/primary → backup/restore → DDC/CI power. Keep the NirSoft path as a fallback behind a setting until each step is verified on real hardware, then remove it.
- Remove
Get-NirSoftTools.ps1, the embedding inPublish-ConsoleMode.ps1/EmbeddedFiles.cs, theMmtMissing/SvvMissingstrings and the NirSoft section ofTHIRD_PARTY_NOTICES.md. - Apply to SignPath.
Testing
None of this can be checked in CI; it needs Windows with real displays. Each PR adds its manual checks to docs/TESTING.md, covering at least: two desk monitors + a TV that starts off, disconnect vs. black-overlay vs. DDC/CI modes, restore after Big Picture / Playnite exit, and an HDMI audio output that only appears once the TV is on. Pure logic (config parsing, layout math) goes into linked files with unit tests.
Done when
- The release build downloads and embeds no NirSoft binary.
- The manual checklist passes on at least one multi-monitor + TV setup.
- The next release can be submitted to SignPath.
Activity
pullfrog commentedon Sep 25, 2026
feat(audio): native Core Audio backend behind a config flag (#91) (#97)
feat(displays): native display backend behind a config flag (#91)
feat(displays): native display backend behind a config flag (#91) (#98)
feat: native audio and displays by default; stop bundling NirSoft too…
feat: 1.6 is all our own code, no NirSoft fallback (#91) (#101)
lippdev commentedon Oct 6, 2026
Done in 1.6.0: displays and audio are controlled through Windows' own APIs and the package no longer bundles MultiMonitorTool or SoundVolumeView (#97, #98, #99, #110, #119). Code signing itself is tracked separately in
docs/CODE_SIGNING.md.