winmr-api/Services/ProcessMonitor.cs

163 lines
5.8 KiB
C#
Raw Permalink Normal View History

2025-07-04 00:04:51 +03:00
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright <20> 2024-2025 Gregory Lirent */
2025-07-02 16:06:50 +03:00
2025-06-30 18:15:07 +03:00
using Microsoft.Extensions.Options;
2025-07-13 14:24:51 +03:00
using System.Collections.Generic;
2025-07-02 16:06:50 +03:00
using System.Runtime.Versioning;
2025-06-30 18:15:07 +03:00
using WebmrAPI.Configuration;
2025-07-03 15:22:40 +03:00
using WebmrAPI.Exceptions;
2025-06-30 18:15:07 +03:00
using WebmrAPI.Models;
2025-07-03 15:22:40 +03:00
using WebmrAPI.Services.Scanners;
using WebmrAPI.Utils;
2025-06-30 18:15:07 +03:00
namespace WebmrAPI.Services
{
2025-07-02 16:06:50 +03:00
[SupportedOSPlatform("windows")]
2025-07-03 20:01:19 +03:00
public class ProcessMonitor : BackgroundService
2025-06-30 18:15:07 +03:00
{
private readonly ILogger<ProcessMonitor> _logger;
2025-07-03 15:22:40 +03:00
private readonly MonitoringSettings _config;
private LazyConcurrentContainer<ProcessInfo> _processes = new();
2025-07-13 14:24:51 +03:00
private LazyConcurrentContainer<WindowInfo> _windows = new();
2025-07-03 15:22:40 +03:00
private ScanProvider _provider;
2025-06-30 18:15:07 +03:00
2025-07-03 15:22:40 +03:00
public ILogger<ProcessMonitor> Logger { get => _logger; }
public MonitoringSettings Config { get => _config; }
public LazyConcurrentContainer<ProcessInfo> Processes { get => _processes; }
2025-07-13 14:24:51 +03:00
public LazyConcurrentContainer<WindowInfo> Windows { get => _windows; }
2025-06-30 18:15:07 +03:00
2025-07-03 15:22:40 +03:00
public IEnumerable<ProcessBaseInfo>? GetBufferedProcesses()
2025-06-30 18:15:07 +03:00
{
2025-07-03 15:22:40 +03:00
return _processes.Values;
2025-07-02 16:06:50 +03:00
}
2025-07-13 14:24:51 +03:00
public IEnumerable<WindowInfo>? GetBufferedWindows()
{
return _windows.Values;
}
2025-07-02 16:06:50 +03:00
2025-07-03 20:01:19 +03:00
async public Task<ProcessInfo?> GetProcessDetails(int pid, ScanTarget target = ScanTarget.ProcessDetails)
2025-07-02 16:06:50 +03:00
{
2025-07-03 15:22:40 +03:00
if (_processes.Container != null && _processes.Container.TryGetValue(pid, out var info))
2025-06-30 18:15:07 +03:00
{
2025-07-03 15:22:40 +03:00
try
{
2025-07-03 20:01:19 +03:00
await _provider.CreateScanTask(info, target).ScanAsync();
2025-07-03 15:22:40 +03:00
_logger.LogInformation($"Scan details of the process {info.Name} (PID: {info.PID}) was completed.");
return info;
}
catch (ProcessMonitorException ex)
2025-06-30 18:15:07 +03:00
{
2025-07-03 15:22:40 +03:00
_logger.LogWarning(ex.Message);
}
catch (Exception ex)
{
_logger.LogError($"Unhandled error during scanning of the process {info.Name} (PID: {info.PID}): {ex.Message}");
2025-06-30 18:15:07 +03:00
}
}
2025-07-03 15:22:40 +03:00
return null;
2025-06-30 18:15:07 +03:00
}
2025-07-13 14:24:51 +03:00
public Task<IEnumerable<BaseWindowInfo>?> GetProcessWindows(int pid)
{
return Task.Run(() =>
{
if (_processes.Container != null && _processes.Container.TryGetValue(pid, out var info))
{
return info.Windows;
}
return null;
});
}
2025-06-30 18:15:07 +03:00
2025-07-02 16:06:50 +03:00
public ProcessMonitor(ILogger<ProcessMonitor> logger, IOptions<AppSettings> settings)
2025-06-30 18:15:07 +03:00
{
_logger = logger;
2025-07-02 16:06:50 +03:00
_config = settings.Value.Monitoring;
2025-07-03 15:22:40 +03:00
_provider = new(this);
2025-06-30 18:15:07 +03:00
}
2025-07-03 20:01:19 +03:00
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2025-06-30 18:15:07 +03:00
{
2025-07-02 16:06:50 +03:00
_logger.LogInformation($"ProcessMonitor started. Scan interval: {_config.ProcessScanInterval} seconds.");
2025-06-30 18:15:07 +03:00
2025-07-03 20:01:19 +03:00
await ScanAsync();
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(_config.ProcessScanInterval));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await ScanAsync();
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("ProcessMonitor is stopping due to cancellation.");
}
catch (Exception ex)
{
_logger.LogError(ex, "ProcessMonitor encountered an unhandled exception and is stopping.");
}
2025-07-02 16:06:50 +03:00
}
2025-06-30 18:15:07 +03:00
2025-07-03 20:01:19 +03:00
private async Task ScanAsync()
2025-07-02 16:06:50 +03:00
{
_logger.LogDebug("Initiating process scan...");
try
{
2025-07-03 20:01:19 +03:00
await _provider.CreateScanTask().ScanAsync();
2025-07-13 14:24:51 +03:00
await new WindowScanner(_provider, _windows).ScanAsync();
Dictionary<int, List<BaseWindowInfo>> pWin = new();
Dictionary<int, List<BaseWindowInfo>> tWin = new();
if (_windows.Values != null)
foreach (var window in _windows.Values)
{
List<BaseWindowInfo> p;
List<BaseWindowInfo> t;
if (!pWin.TryGetValue(window.PID, out p))
{
pWin.Add(window.PID, p = new());
}
if (!tWin.TryGetValue(window.ThreadId, out t))
{
tWin.Add(window.ThreadId, t = new());
}
p.Add(window);
t.Add(window);
}
if (_processes.Values != null)
foreach (var proc in _processes.Values)
{
if (pWin.TryGetValue(proc.PID, out var pws))
{
proc.Windows = pws;
if (proc.ThreadsContainer.Values != null)
foreach (var thread in proc.ThreadsContainer.Values)
{
if (tWin.TryGetValue(thread.ID, out var tws))
{
thread.Windows = tws;
}
}
}
}
2025-07-03 15:22:40 +03:00
_logger.LogInformation($"Process buffer updated, contains {Processes.Container?.Count} processes.");
2025-06-30 18:15:07 +03:00
}
2025-07-03 15:22:40 +03:00
catch (ProcessMonitorException ex)
2025-06-30 18:15:07 +03:00
{
2025-07-03 15:22:40 +03:00
_logger.LogWarning(ex.Message);
2025-06-30 18:15:07 +03:00
}
2025-07-02 16:06:50 +03:00
catch (Exception ex)
2025-06-30 18:15:07 +03:00
{
2025-07-03 15:22:40 +03:00
_logger.LogError($"Unhandled error during process monitoring cycle: {ex.Message}");
2025-06-30 18:15:07 +03:00
}
}
}
2025-07-02 16:06:50 +03:00
}