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:
co-authored by
Claude Opus 4.8
parent
fa554cbe35
commit
9e75331daf
@@ -768,6 +768,22 @@ internal static class SelfTest
|
||||
Check(ServiceControl.AddUserStartStopAce(amended) == amended, "adding the ACE twice must be a no-op (idempotent)");
|
||||
Check(ServiceControl.AddUserStartStopAce("garbage") is null, "a non-DACL SDDL must be rejected");
|
||||
|
||||
// 2b. The app-source path (which the SYSTEM service watches for auto-updates) round-trips, and drives
|
||||
// the update check: unknown/empty source => no update, so the service never acts on uncertainty.
|
||||
var savedOverride = ServiceStore.TestDirectoryOverride;
|
||||
var storeTmp = Path.Combine(Path.GetTempPath(), "remsound-appsrc-" + Guid.NewGuid().ToString("N"));
|
||||
try
|
||||
{
|
||||
ServiceStore.TestDirectoryOverride = storeTmp;
|
||||
Check(ServiceStore.LoadAppSourcePath() is null, "no app-source recorded yet must read back null");
|
||||
ServiceStore.SaveAppSourcePath(@"C:\Some\App\Folder");
|
||||
Check(ServiceStore.LoadAppSourcePath() == @"C:\Some\App\Folder", "the app-source path must round-trip");
|
||||
// Points at a folder with no RemSound.exe => version unreadable => no update landed (never act on uncertainty).
|
||||
Check(ServiceUpdate.OnDiskVersion() is null, "an app-source folder with no RemSound.exe must yield no version");
|
||||
Check(!ServiceUpdate.UpdateLanded(), "with no readable app version, no update must be detected");
|
||||
}
|
||||
finally { ServiceStore.TestDirectoryOverride = savedOverride; try { Directory.Delete(storeTmp, recursive: true); } catch { } }
|
||||
|
||||
// 3. CopyProgramTo copies program files but NEVER the user-state folders.
|
||||
var root = Path.Combine(Path.GetTempPath(), "remsound-svccopy-" + Guid.NewGuid().ToString("N"));
|
||||
var src = Path.Combine(root, "src");
|
||||
|
||||
Reference in New Issue
Block a user