Review finding, same class as the shipped install-hang fix: two paths still ran the
elevated service helper synchronously on the UI thread, freezing the window and live
audio for the duration (worst case minutes across two UAC prompts).
1) Service-profile save (MainForm.ConfigureServiceProfile): saving while the service
runs did RunElevated(stop) + RunElevated(start) inline. Now: the restart runs on a
background task, and tries a NO-UAC restart first - ServiceControl.TryRestartNoAdmin
uses the start/stop rights the installer grants the installing account, so the
normal case has no elevation prompt at all. Elevated verbs remain the fallback
(service installed by a different account). Success is silent; only a failed
restart reports back. The save popup now says the service is restarting.
2) App installer's optional service step (AppInstaller): the install + start-now calls
ran RunElevated inline. The flow is sequential (can't fire-and-forget - the installer
relaunches and exits afterwards), so RunElevatedResponsive runs the helper on a
worker while a small modal "working..." shell pumps messages: UI and audio stay
live, nothing can be double-triggered, NVDA announces the step, and the exit code
still returns inline.
Test: "No-admin service restart fails safe" - TryRestartNoAdmin against a missing
service returns false promptly without throwing (that false routes callers onto the
elevated fallback). The success path needs the real SCM + grant, covered by hand-test.
Gate 47/47.
Part of the review-fix batch; no release until the whole plan lands.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- After a successful install, both entry points now ask "start it now?" (Yes/No):
the app's Service menu (RunServiceVerbAsync reused for the follow-up start) and the
app installer's optional service step. Without this the service only comes up at the
next boot, so a first-time user saw nothing happen after installing it.
- Uninstall already stops the service before deleting (DoUninstall -> DoStop, which
waits up to 15s for Stopped), so nothing to change there - confirmed, not added.
- Manual: note the start-now prompt in the service section.
No new automated test: these are UI Yes/No prompts layered on the already-tested
start/stop verbs. Gate 46/46.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The service was registered to run from wherever it was installed FROM (Environment
.ProcessPath), so installing from a dev/test folder pinned it there: it locked those
files (blocking every rebuild) and, for a real user installing from the app folder,
would lock the app's own binaries and break the auto-updater. Stopping it also needed
admin, so only the app's UAC-prompting Service menu could do it.
Fixes:
- The service now installs its OWN copy of the program into ProgramData\RemSound\
service\bin and is registered to run from there. Never touches the install/dev
folder again. CopyProgramTo copies the exe + DLLs + runtimes + default sounds but
excludes user-state folders; uninstall removes the bin copy.
- Install grants Authenticated Users start/stop/query on the service (sc sdset, ACE
merged into the existing DACL) so it can be stopped with a plain `sc stop
RemSoundService` or the Service menu -- no admin, no app. Pure SDDL-amend helper is
unit-tested (placement + idempotency).
- The app installer now asks, after install, whether to also install the service
(optional, its own UAC step; declining is fine -- Service menu installs it later).
- deploy-test.ps1: only a copy running FROM the publish folder locks its binaries, so
only that forces a sounds-only deploy. The service (ProgramData) and an installed app
no longer make the script silently skip the binary publish -- the bug that had us
testing stale builds for rounds.
New self-test "Service self-contained install" (runs-from-own-bin, SDDL amend, copy
exclusions). Gate: 40/40.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified findings from a multi-dimension code audit, plus the two install-flow bugs:
- Fix Opus encoder use-after-free on a codec/rate change while streaming (guard swap vs encode).
- Fix "both" single-file recording dropping audio + drifting (drain both directions in lockstep).
- Fix broken clip counter, UPnP teardown on exit, auto-update-restart foreground grant, and a
malformed-Opus-format packet orphaning a playout session forever.
- Post-install relaunch now respects start-minimised; uninstall is path-aware so it won't clear a
different copy's run-at-startup.
- Perf/hygiene: cache AppConfig off UI hot paths, fold per-peer EQ+gain into one pass, deterministic
disposal (tray menu, timers, COM shortcut, Process handles, process meter), ring-buffer overflow
guard, receiver session-lock fix, remote-control allow-list moved onto the UI thread.
- Remove dead code (two IsAsioBackend, SessionPlayout.Reset, IsSameEndpoint, RemSoundUpdater
IDisposable); several stale-doc fixes.
Deferred (not in this release): drift-estimator tweak, peer-discovery pruning, uninstall retry-loop,
encryption nonce. Wire format unchanged (interops v3.3-v5.1). Version -> 5.2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New Options -> Install / Uninstall RemSound on this PC: a per-user self-installer
(%LOCALAPPDATA%\Programs\RemSound, no admin) with optional desktop + Start-menu
shortcuts, login auto-start (reuses StartupAutoStart), Windows Installed-apps
registration, and copy-across of profiles+config, recordings and logs. Install
state is decided by a marker file, not a folder-path guess; the post-install
relaunch hands over foreground via AllowSetForegroundWindow so the installed copy
comes to the front; uninstall uses a batch remover (no PowerShell) and confirms
with two independent tick-boxes. All new dialogs use the house accessible controls
(AccessibleCheckBox, Theme.Heading).
Also: iOS (TestFlight) companion link alongside Android in README + manual;
slimmed-down default cue WAVs; About/RELEASE_NOTES/manual updated; version -> 5.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>