Some checks failed
Release / release (push) Failing after 0s
Worker: - Wire UseWindowsService + Microsoft.Extensions.Hosting.WindowsServices so SCM's Service Control Protocol handshake succeeds. Previously the binary exited immediately under sc start, leaving the service registered but never running. Installer: - Pin SDK to .NET 9 (global.json) — SDK 10 dropped win-arm from its RID graph, breaking restore of the WPF project; .NET 9 keeps win-arm AND understands the .slnx solution format. - Force SelfContained=true and default RID=win-x64 when PublishSingleFile is set, so Rider Publish and CLI produce the same bundle. - Dark theme: set Background/Foreground explicitly on WizardWindow and SettingsWindow roots (WPF implicit styles don't cascade to derived Window types). Custom ComboBox template + ComboBoxItem style so dropdowns honour the dark palette instead of system defaults. - Throttle download progress to one report per MB and overwrite the same UI line (\r prefix marker) instead of appending per chunk. - Register ClaudeDo in HKLM\...\Uninstall so it appears in Apps & Features. Copy installer into InstallDir\uninstaller\ for the UninstallString, and schedule a cmd.exe trampoline to handle the self-delete case when Apps & Features launches the copy from inside the install dir. - Treat sc.exe stop exit 1062 (ERROR_SERVICE_NOT_ACTIVE) as success. - Delete the uninstall registry key during UninstallRunner. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
55 lines
2.0 KiB
C#
55 lines
2.0 KiB
C#
using ClaudeDo.Installer.Core;
|
|
|
|
namespace ClaudeDo.Installer.Steps;
|
|
|
|
public sealed class StopServiceStep : IInstallStep
|
|
{
|
|
public const string ServiceName = "ClaudeDoWorker";
|
|
|
|
public string Name => "Stop Worker Service";
|
|
|
|
public async Task<StepResult> ExecuteAsync(InstallContext ctx, IProgress<string> progress, CancellationToken ct)
|
|
{
|
|
progress.Report($"Stopping {ServiceName} (if running)...");
|
|
|
|
// sc.exe query -> returns non-zero if the service does not exist; that's fine.
|
|
var (queryExit, queryOutput) = await ProcessRunner.RunAsync("sc.exe", $"query {ServiceName}", null, progress, ct);
|
|
if (queryExit != 0)
|
|
{
|
|
progress.Report("Service is not registered — nothing to stop.");
|
|
return StepResult.Ok();
|
|
}
|
|
|
|
if (queryOutput.Contains("STOPPED", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
progress.Report("Service is already stopped.");
|
|
return StepResult.Ok();
|
|
}
|
|
|
|
var (stopExit, _) = await ProcessRunner.RunAsync("sc.exe", $"stop {ServiceName}", null, progress, ct);
|
|
// 1062 = ERROR_SERVICE_NOT_ACTIVE — registered but not running, treat as already stopped.
|
|
if (stopExit == 1062)
|
|
{
|
|
progress.Report("Service was registered but not running.");
|
|
return StepResult.Ok();
|
|
}
|
|
if (stopExit != 0)
|
|
return StepResult.Fail($"sc.exe stop failed with exit code {stopExit}");
|
|
|
|
// Poll until stopped or timeout (up to 30s).
|
|
for (var i = 0; i < 30; i++)
|
|
{
|
|
ct.ThrowIfCancellationRequested();
|
|
await Task.Delay(1000, ct);
|
|
var (e, o) = await ProcessRunner.RunAsync("sc.exe", $"query {ServiceName}", null, progress, ct);
|
|
if (e != 0 || o.Contains("STOPPED", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
progress.Report("Service stopped.");
|
|
return StepResult.Ok();
|
|
}
|
|
}
|
|
|
|
return StepResult.Fail("Service did not stop within 30 seconds.");
|
|
}
|
|
}
|