Service auto-updates itself when the app updates (no UAC, no menu click)

The self-contained-service change broke the old self-update: the service used to run in
the app's own folder and restart onto a newer RemSound.exe that landed "next to it";
now it runs from its own ProgramData copy, so it never saw the app's new build.

Rework: at install/update the service records the app's folder (ServiceStore app-source
path, written elevated so the SYSTEM account can read it). The service's existing 45s
update poll now watches THAT folder; when the app's auto-updater drops a strictly-newer
RemSound.exe there, the service (as SYSTEM) copies the new build into its own bin and
restarts onto it via the detached restarter script. All SYSTEM-side: no UAC, no user
action. Loop-safe (strictly-newer only; bin == app version after the copy).

So a real release (version bump) propagates to the service automatically. Same-version
dev rebuilds don't trip the strictly-newer check -- the Service menu "Update service to
this version" forces those.

Trust posture unchanged from the old in-place scheme (SYSTEM copies from a user-writable
folder); noted in the class doc for a future code-signed hardening.

New self-test coverage: app-source path round-trip + "no readable app version => no
update" (never act on uncertainty). Gate: 40/40.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-17 15:03:37 +01:00
co-authored by Claude Opus 4.8
parent fa554cbe35
commit 9e75331daf
4 changed files with 71 additions and 10 deletions
+4
View File
@@ -107,6 +107,9 @@ public static class ServiceControl
// folder, so the running service uses ITS copy, not the source it was installed from.
try { CopyProgramTo(sourceDir, ServiceStore.BinDirectory); }
catch (Exception ex) { ServiceStore.AppendServiceEvent($"install: copy program failed: {ex.GetType().Name}: {ex.Message}"); return 5; }
// Remember where the app lives, so the SYSTEM service can watch it and auto-update itself when the
// app's auto-updater drops a newer build there (no UAC — see ServiceUpdate).
ServiceStore.SaveAppSourcePath(sourceDir);
var rc = RunSc(BuildCreateArgs(ServiceStore.BinExePath));
if (rc != 0) return rc;
@@ -222,6 +225,7 @@ public static class ServiceControl
try { DoStart(); } catch { /* leave it stopped rather than half-updated */ }
return 5;
}
ServiceStore.SaveAppSourcePath(sourceDir); // keep the auto-update watch pointed at the current app
return DoStart();
}