using System.Diagnostics;
using System.ServiceProcess;
using SpdUp.Core;
using SpdUp.Models;
namespace SpdUp.Services;
///
/// Manages the full boost lifecycle: activate → monitor → restore.
///
public sealed class OptimizationService
{
private readonly RamOptimizationService _ramService = new();
private readonly AppSettings _settings;
private readonly List _stoppedServices = new();
private uint _previousTimerResolution;
private bool _timerResolutionChanged;
private string? _boostedProcessName;
private bool _isBoosted;
public bool IsBoosted => _isBoosted;
public string? BoostedProcessName => _boostedProcessName;
public event Action? BoostActivated;
public event Action? BoostDeactivated;
public OptimizationService(AppSettings settings)
{
_settings = settings;
}
///
/// Activate all boost optimizations. Thread-safe.
///
public async Task ActivateBoostAsync(string? gameProcessName = null)
{
if (_isBoosted) return new BoostResult(false, "Boost is already active.");
var messages = new List();
try
{
// 1. Enable required privileges
NativeMethods.EnablePrivilege(NativeMethods.SE_DEBUG_NAME);
NativeMethods.EnablePrivilege(NativeMethods.SE_INC_BASE_PRIORITY_NAME);
NativeMethods.EnablePrivilege(NativeMethods.SE_PROF_SINGLE_PROCESS_NAME);
// 2. RAM Cleanup
await Task.Run(() =>
{
var result = _ramService.FullCleanup();
messages.Add($"RAM: trimmed {result.TrimmedCount} processes, standby purge {(result.StandbyPurged > 0 ? "OK" : "skipped")}.");
});
// 3. Stop non-critical services
await Task.Run(() =>
{
foreach (var svcName in _settings.ServicesToStop)
{
try
{
using var sc = new ServiceController(svcName);
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
_stoppedServices.Add(svcName);
messages.Add($"Stopped service: {sc.DisplayName}");
}
}
catch (Exception ex)
{
Logger.Warn($"Could not stop {svcName}: {ex.Message}");
}
}
});
// 4. Set game to HIGH priority
if (!string.IsNullOrEmpty(gameProcessName))
{
_boostedProcessName = gameProcessName;
await Task.Run(() => SetProcessPriority(gameProcessName, ProcessPriorityClass.High));
messages.Add($"Set {gameProcessName} to HIGH priority.");
}
// 5. Reduce timer resolution (0.5 ms)
await Task.Run(() =>
{
NativeMethods.NtQueryTimerResolution(out _, out _, out _previousTimerResolution);
int status = NativeMethods.NtSetTimerResolution(5000, true, out _);
_timerResolutionChanged = status == 0;
if (_timerResolutionChanged)
messages.Add("Timer resolution set to 0.5 ms.");
});
_isBoosted = true;
BoostActivated?.Invoke(string.Join("\n", messages));
Logger.Info("Boost activated: " + string.Join(" | ", messages));
return new BoostResult(true, string.Join("\n", messages));
}
catch (Exception ex)
{
Logger.Error("Boost activation failed", ex);
return new BoostResult(false, $"Error: {ex.Message}");
}
}
///
/// Restore all changes made during boost.
///
public async Task DeactivateBoostAsync()
{
if (!_isBoosted) return;
try
{
// 1. Restore timer resolution
if (_timerResolutionChanged)
{
NativeMethods.NtSetTimerResolution(_previousTimerResolution, true, out _);
_timerResolutionChanged = false;
Logger.Info("Timer resolution restored.");
}
// 2. Restart stopped services
await Task.Run(() =>
{
foreach (var svcName in _stoppedServices)
{
try
{
using var sc = new ServiceController(svcName);
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
Logger.Info($"Restarted service: {sc.DisplayName}");
}
}
catch (Exception ex)
{
Logger.Warn($"Could not restart {svcName}: {ex.Message}");
}
}
_stoppedServices.Clear();
});
// 3. Restore process priority
if (_boostedProcessName is not null)
{
await Task.Run(() => SetProcessPriority(_boostedProcessName, ProcessPriorityClass.Normal));
_boostedProcessName = null;
}
_isBoosted = false;
BoostDeactivated?.Invoke();
Logger.Info("Boost deactivated – all settings restored.");
}
catch (Exception ex)
{
Logger.Error("Boost deactivation error", ex);
}
}
///
/// Set priority class for a process by name.
///
public static void SetProcessPriority(string processName, ProcessPriorityClass priority)
{
foreach (var proc in Process.GetProcessesByName(processName))
{
try
{
proc.PriorityClass = priority;
Logger.Info($"Set {processName} (PID {proc.Id}) priority to {priority}.");
}
catch (Exception ex)
{
Logger.Warn($"Cannot set priority for {processName}: {ex.Message}");
}
finally
{
proc.Dispose();
}
}
}
///
/// Kill a process by PID.
///
public static bool KillProcess(int pid)
{
try
{
using var proc = Process.GetProcessById(pid);
proc.Kill(entireProcessTree: true);
Logger.Info($"Killed process {proc.ProcessName} (PID {pid}).");
return true;
}
catch (Exception ex)
{
Logger.Error($"Failed to kill PID {pid}", ex);
return false;
}
}
}
public record BoostResult(bool Success, string Message);