Service owns its binaries + is stoppable without admin; installer offers the service
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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a450cae66a
commit
4e15451e1d
@@ -170,6 +170,33 @@ internal static class AppInstaller
|
||||
"Press OK to finish. RemSound will now close and reopen from the new install location.",
|
||||
"RemSound installed", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
|
||||
// Offer to install the send-only Windows service too (Ed, 2026-07-17). It's a separate, optional
|
||||
// component that needs its own admin (UAC) step, so we ask rather than assume. Declining is fine —
|
||||
// it can be installed any time from the app's Service menu. Never block the app install on it.
|
||||
try
|
||||
{
|
||||
if (!ServiceControl.IsInstalled())
|
||||
{
|
||||
var wantService = MessageBox.Show(owner,
|
||||
"Do you also want to install the RemSound service?" + Environment.NewLine + Environment.NewLine +
|
||||
"The service streams this PC's audio to your RemSound peers even when nobody is logged in " +
|
||||
"(for example at the lock screen after a reboot). It's send-only and steps aside whenever the " +
|
||||
"RemSound app is open. You can install or remove it later from the app's Service menu.",
|
||||
"Install the RemSound service?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (wantService == DialogResult.Yes)
|
||||
{
|
||||
log?.Invoke("install: user opted to install the service too");
|
||||
var rc = ServiceControl.RunElevated(ServiceControl.InstallVerb);
|
||||
MessageBox.Show(owner,
|
||||
rc == 0
|
||||
? "The RemSound service was installed. Configure and start it from the app's Service menu when you want it running."
|
||||
: "The RemSound service was not installed (the elevation prompt was declined, or it failed). You can try again later from the app's Service menu.",
|
||||
"RemSound service", MessageBoxButtons.OK, rc == 0 ? MessageBoxIcon.Information : MessageBoxIcon.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { log?.Invoke($"install: optional service step skipped ({ex.GetType().Name}: {ex.Message})"); }
|
||||
|
||||
// Hand over cleanly. We can't just launch the installed exe and exit: the single-instance
|
||||
// lock would still be held for the instant it takes us to shut down, and the new copy would
|
||||
// see "already running". So a tiny batch waits for THIS process to exit (lock released), then
|
||||
|
||||
Reference in New Issue
Block a user