diff --git a/src/Artemis.Core/Services/ProcessMonitor/Events/ProcessEventArgs.cs b/src/Artemis.Core/Services/ProcessMonitor/Events/ProcessEventArgs.cs new file mode 100644 index 000000000..beb5e42e9 --- /dev/null +++ b/src/Artemis.Core/Services/ProcessMonitor/Events/ProcessEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace Artemis.Core.Services +{ + public class ProcessEventArgs : EventArgs + { + public string ProcessName { get; } + + public ProcessEventArgs(string name) + { + ProcessName = name; + } + } +} diff --git a/src/Artemis.Core/Services/ProcessMonitor/Interfaces/IProcessMonitorService.cs b/src/Artemis.Core/Services/ProcessMonitor/Interfaces/IProcessMonitorService.cs new file mode 100644 index 000000000..c1873de68 --- /dev/null +++ b/src/Artemis.Core/Services/ProcessMonitor/Interfaces/IProcessMonitorService.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Artemis.Core.Services +{ + public interface IProcessMonitorService : IArtemisService + { + void AddProcessWatcher(ProcessWatcher provider); + void RemoveProcessWatcher(ProcessWatcher provider); + + IEnumerable GetRunningProcesses(); + + event EventHandler ProcessStarted; + + event EventHandler ProcessStopped; + } +} diff --git a/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs new file mode 100644 index 000000000..65df1ae65 --- /dev/null +++ b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Artemis.Core.Services +{ + public class ProcessMonitorService : IProcessMonitorService + { + private readonly List _watchers = new List(); + public event EventHandler ProcessStarted; + public event EventHandler ProcessStopped; + + public void AddProcessWatcher(ProcessWatcher watcher) + { + watcher.ProcessStarted += ProviderOnProcessStarted; + watcher.ProcessStopped += ProviderOnProcessStopped; + _watchers.Add(watcher); + } + + public void RemoveProcessWatcher(ProcessWatcher watcher) + { + if (!_watchers.Contains(watcher)) + return; + + _watchers.Remove(watcher); + watcher.ProcessStarted -= ProviderOnProcessStarted; + watcher.ProcessStopped -= ProviderOnProcessStopped; + } + + public IEnumerable GetRunningProcesses() + { + return _watchers.SelectMany(w => w.GetRunningProcesses()); + } + + private void ProviderOnProcessStopped(object? sender, ProcessEventArgs e) + { + ProcessStopped?.Invoke(this, e); + } + + private void ProviderOnProcessStarted(object? sender, ProcessEventArgs e) + { + ProcessStarted?.Invoke(this, e); + } + } +} diff --git a/src/Artemis.Core/Services/ProcessMonitor/ProcessWatcher.cs b/src/Artemis.Core/Services/ProcessMonitor/ProcessWatcher.cs new file mode 100644 index 000000000..7bede154a --- /dev/null +++ b/src/Artemis.Core/Services/ProcessMonitor/ProcessWatcher.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Artemis.Core.Services +{ + /// + /// Class that provides info on running processes, including events on event start and stop + /// + public abstract class ProcessWatcher + { + public event EventHandler ProcessStarted; + public event EventHandler ProcessStopped; + + public abstract IEnumerable GetRunningProcesses(); + + public virtual void OnProcessStarted(string processName) + { + ProcessStarted?.Invoke(this, new ProcessEventArgs(processName)); + } + + public virtual void OnProcessStopped(string processName) + { + ProcessStopped?.Invoke(this, new ProcessEventArgs(processName)); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs index 585b1b1a4..b540db62c 100644 --- a/src/Artemis.UI/Bootstrapper.cs +++ b/src/Artemis.UI/Bootstrapper.cs @@ -89,6 +89,7 @@ namespace Artemis.UI IRegistrationService registrationService = Kernel.Get(); registrationService.RegisterInputProvider(); + registrationService.RegisterProcessWatchers(); registrationService.RegisterControllers(); // Initialize background services diff --git a/src/Artemis.UI/ProcessWatchers/PollingProcessWatcher.cs b/src/Artemis.UI/ProcessWatchers/PollingProcessWatcher.cs new file mode 100644 index 000000000..20a4e807a --- /dev/null +++ b/src/Artemis.UI/ProcessWatchers/PollingProcessWatcher.cs @@ -0,0 +1,52 @@ +using Artemis.Core.Services; +using Serilog; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Text; +using System.Threading.Tasks; +using System.Timers; + +namespace Artemis.UI.ProcessWatchers +{ + public class PollingProcessWatcher : ProcessWatcher + { + private readonly ILogger _logger; + private readonly Timer _processScanTimer; + private IEnumerable _lastScannedProcesses; + + public PollingProcessWatcher(ILogger logger) + { + _logger = logger; + _lastScannedProcesses = Process.GetProcesses().Select(p => p.ProcessName).Distinct().ToArray(); + _processScanTimer = new Timer(100); + _processScanTimer.Elapsed += OnTimerElapsed; + _processScanTimer.Start(); + } + + private void OnTimerElapsed(object sender, ElapsedEventArgs e) + { + var newProcesses = Process.GetProcesses().Select(p => p.ProcessName).Distinct(); + foreach (var startedProcess in newProcesses.Except(_lastScannedProcesses)) + { + OnProcessStarted(startedProcess); + _logger.Information($"Started Process!: {startedProcess}"); + } + + foreach (var stoppedProcess in _lastScannedProcesses.Except(newProcesses)) + { + OnProcessStopped(stoppedProcess); + _logger.Information($"Stopped Process!: {stoppedProcess}"); + } + + _lastScannedProcesses = newProcesses.ToArray(); + } + + public override IEnumerable GetRunningProcesses() + { + return _lastScannedProcesses; + } + } +} diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index 0d51ce376..b8a1e8b86 100644 --- a/src/Artemis.UI/Services/RegistrationService.cs +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -7,6 +7,7 @@ using Artemis.UI.DefaultTypes.DataModel.Input; using Artemis.UI.DefaultTypes.PropertyInput; using Artemis.UI.InputProviders; using Artemis.UI.Ninject; +using Artemis.UI.ProcessWatchers; using Artemis.UI.Shared.Services; using Serilog; @@ -20,6 +21,7 @@ namespace Artemis.UI.Services private readonly IPluginManagementService _pluginManagementService; private readonly IInputService _inputService; private readonly IWebServerService _webServerService; + private readonly IProcessMonitorService _processMonitorService; private bool _registeredBuiltInDataModelDisplays; private bool _registeredBuiltInDataModelInputs; private bool _registeredBuiltInPropertyEditors; @@ -29,7 +31,8 @@ namespace Artemis.UI.Services IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService, IInputService inputService, - IWebServerService webServerService) + IWebServerService webServerService, + IProcessMonitorService processMonitorService) { _logger = logger; _dataModelUIService = dataModelUIService; @@ -37,6 +40,7 @@ namespace Artemis.UI.Services _pluginManagementService = pluginManagementService; _inputService = inputService; _webServerService = webServerService; + _processMonitorService = processMonitorService; LoadPluginModules(); pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling; @@ -92,6 +96,11 @@ namespace Artemis.UI.Services _inputService.AddInputProvider(new NativeWindowInputProvider(_logger, _inputService)); } + public void RegisterProcessWatchers() + { + _processMonitorService.AddProcessWatcher(new PollingProcessWatcher(_logger)); + } + public void RegisterControllers() { _webServerService.AddController(); @@ -115,6 +124,7 @@ namespace Artemis.UI.Services void RegisterBuiltInDataModelInputs(); void RegisterBuiltInPropertyEditors(); void RegisterInputProvider(); + void RegisterProcessWatchers(); void RegisterControllers(); } } \ No newline at end of file