| | | 1 | | using System; |
| | | 2 | | using Microsoft.Win32; |
| | | 3 | | using SwitchBlade.Contracts; |
| | | 4 | | |
| | | 5 | | namespace SwitchBlade.Services |
| | | 6 | | { |
| | | 7 | | /// <summary> |
| | | 8 | | /// Manages Windows startup registry entries for the application. |
| | | 9 | | /// Extracted from SettingsService to follow Single Responsibility Principle. |
| | | 10 | | /// </summary> |
| | | 11 | | public class WindowsStartupManager : IWindowsStartupManager |
| | | 12 | | { |
| | | 13 | | private const string APP_REGISTRY_KEY = @"Software\SwitchBlade"; |
| | | 14 | | private const string STARTUP_REGISTRY_KEY = @"Software\Microsoft\Windows\CurrentVersion\Run"; |
| | | 15 | | private const string STARTUP_VALUE_NAME = "SwitchBlade"; |
| | | 16 | | |
| | | 17 | | private readonly IRegistryService _registryService; |
| | | 18 | | private readonly ILogger? _logger; |
| | | 19 | | |
| | 21 | 20 | | public WindowsStartupManager(IRegistryService registryService, ILogger? logger = null) |
| | 21 | 21 | | { |
| | 21 | 22 | | _registryService = registryService ?? throw new ArgumentNullException(nameof(registryService)); |
| | 20 | 23 | | _logger = logger; |
| | 20 | 24 | | } |
| | | 25 | | |
| | | 26 | | /// <inheritdoc /> |
| | | 27 | | public bool IsStartupEnabled() |
| | 4 | 28 | | { |
| | | 29 | | try |
| | 4 | 30 | | { |
| | 4 | 31 | | return _registryService.GetCurrentUserValue(STARTUP_REGISTRY_KEY, STARTUP_VALUE_NAME) != null; |
| | | 32 | | } |
| | 1 | 33 | | catch |
| | 1 | 34 | | { |
| | 1 | 35 | | return false; |
| | | 36 | | } |
| | 4 | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <inheritdoc /> |
| | | 40 | | public void EnableStartup(string executablePath) |
| | 5 | 41 | | { |
| | 5 | 42 | | if (string.IsNullOrEmpty(executablePath)) |
| | 2 | 43 | | { |
| | 2 | 44 | | throw new ArgumentException("Executable path cannot be null or empty.", nameof(executablePath)); |
| | | 45 | | } |
| | | 46 | | |
| | | 47 | | try |
| | 3 | 48 | | { |
| | | 49 | | // Add /minimized so app starts in background on Windows startup |
| | 3 | 50 | | _registryService.SetCurrentUserValue(STARTUP_REGISTRY_KEY, STARTUP_VALUE_NAME, $"\"{executablePath}\" /m |
| | 1 | 51 | | } |
| | 2 | 52 | | catch (Exception ex) |
| | 2 | 53 | | { |
| | 2 | 54 | | _logger?.LogError("Failed to enable startup registry entry", ex); |
| | 2 | 55 | | } |
| | 3 | 56 | | } |
| | | 57 | | |
| | | 58 | | /// <inheritdoc /> |
| | | 59 | | public void DisableStartup() |
| | 4 | 60 | | { |
| | | 61 | | try |
| | 4 | 62 | | { |
| | 4 | 63 | | _registryService.DeleteCurrentUserValue(STARTUP_REGISTRY_KEY, STARTUP_VALUE_NAME, false); |
| | 2 | 64 | | } |
| | 2 | 65 | | catch (Exception ex) |
| | 2 | 66 | | { |
| | 2 | 67 | | _logger?.LogError("Failed to disable startup registry entry", ex); |
| | 2 | 68 | | } |
| | 4 | 69 | | } |
| | | 70 | | |
| | | 71 | | /// <inheritdoc /> |
| | | 72 | | public bool CheckAndApplyStartupMarker() |
| | 7 | 73 | | { |
| | | 74 | | try |
| | 7 | 75 | | { |
| | 7 | 76 | | object? markerValue = _registryService.GetCurrentUserValue(APP_REGISTRY_KEY, "EnableStartupOnFirstRun"); |
| | 5 | 77 | | if (markerValue != null) |
| | 3 | 78 | | { |
| | | 79 | | // Check if it's "1" (string from MSI) or 1 (integer) |
| | 3 | 80 | | string markerStr = markerValue.ToString() ?? "0"; |
| | 3 | 81 | | bool shouldEnable = markerStr == "1"; |
| | | 82 | | |
| | | 83 | | // Always delete the marker after checking (it's a one-time flag) |
| | 3 | 84 | | _registryService.DeleteCurrentUserValue(APP_REGISTRY_KEY, "EnableStartupOnFirstRun", false); |
| | | 85 | | |
| | 3 | 86 | | return shouldEnable; |
| | | 87 | | } |
| | 2 | 88 | | } |
| | 2 | 89 | | catch (Exception ex) |
| | 2 | 90 | | { |
| | 2 | 91 | | _logger?.LogError("Failed to read/delete EnableStartupOnFirstRun marker", ex); |
| | 2 | 92 | | } |
| | | 93 | | |
| | 4 | 94 | | return false; |
| | 7 | 95 | | } |
| | | 96 | | } |
| | | 97 | | } |