Service: one-click Update-service-to-this-version (no uninstall/reinstall)
Because the service now runs from its own admin-only copy, getting a new build into it meant uninstall+reinstall (two UAC prompts). Add a DoUpdate path + Service-menu item that does it in one: stop -> overwrite the ProgramData bin copy from the running app -> start. Wired as the --update-service verb (elevated), recognised by the service-verb gate. Enabled only when the service is installed; falls back to a plain install if not. Note: the service bin folder stays admin-only writable on purpose (a user-writable SYSTEM binary would be a privilege-escalation hole), so the copy is done elevated via the one UAC prompt rather than in-process. 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
b8d0fa5a65
commit
fa554cbe35
@@ -33,6 +33,7 @@ public static class ServiceControl
|
||||
/// the Program.cs dispatcher agree.</summary>
|
||||
public const string InstallVerb = "--install-service";
|
||||
public const string UninstallVerb = "--uninstall-service";
|
||||
public const string UpdateVerb = "--update-service";
|
||||
public const string StartVerb = "--start-service";
|
||||
public const string StopVerb = "--stop-service";
|
||||
public const string RunVerb = "--run-service";
|
||||
@@ -201,6 +202,29 @@ public static class ServiceControl
|
||||
return rc;
|
||||
}
|
||||
|
||||
/// <summary>Refreshes the service's OWN copy of the program with the CURRENTLY-running build, without
|
||||
/// an uninstall/reinstall: stop → overwrite the binaries in <see cref="ServiceStore.BinDirectory"/>
|
||||
/// from this exe's folder → start. Must be run elevated (writing into the admin-only service bin
|
||||
/// folder, and stop/start). Returns 0 on success. Used by the Service menu's "Update service" so a new
|
||||
/// build reaches the service in one UAC prompt.</summary>
|
||||
public static int DoUpdate()
|
||||
{
|
||||
if (!IsInstalled()) return DoInstall(); // not installed yet — a plain install does the copy too
|
||||
var exe = Environment.ProcessPath;
|
||||
var sourceDir = string.IsNullOrEmpty(exe) ? null : Path.GetDirectoryName(exe);
|
||||
if (string.IsNullOrEmpty(sourceDir)) return 2;
|
||||
|
||||
try { DoStop(); } catch { /* best-effort — copy may still fail if files stay locked, handled below */ }
|
||||
try { CopyProgramTo(sourceDir, ServiceStore.BinDirectory); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceStore.AppendServiceEvent($"update: copy program failed: {ex.GetType().Name}: {ex.Message}");
|
||||
try { DoStart(); } catch { /* leave it stopped rather than half-updated */ }
|
||||
return 5;
|
||||
}
|
||||
return DoStart();
|
||||
}
|
||||
|
||||
/// <summary>Starts the service. Must be run elevated. Returns 0 on success.</summary>
|
||||
public static int DoStart()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user