From 3d61d60c86f00f5dbf409bc1b6e6a26ad13d37f1 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 14 Feb 2023 18:16:13 +0100 Subject: [PATCH] feat: rewrite NostaleProcesses to work with ManagementEventWatcher events instead of polling --- src/PacketLogger/Models/NostaleProcesses.cs | 189 ++++++++++++-------- src/PacketLogger/Views/DocumentView.axaml | 7 +- 2 files changed, 124 insertions(+), 72 deletions(-) diff --git a/src/PacketLogger/Models/NostaleProcesses.cs b/src/PacketLogger/Models/NostaleProcesses.cs index 80c7855..dd72508 100644 --- a/src/PacketLogger/Models/NostaleProcesses.cs +++ b/src/PacketLogger/Models/NostaleProcesses.cs @@ -9,14 +9,18 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using System.Management; using System.Reactive.Concurrency; using System.Reactive.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; using System.Threading.Tasks; using NosSmooth.Comms.Local; using NosSmooth.Core.Extensions; using NosSmooth.LocalBinding; using NosSmooth.LocalBinding.Errors; using NosSmooth.LocalBinding.Options; +using NosSmooth.LocalBinding.Structs; using ReactiveUI; namespace PacketLogger.Models; @@ -26,109 +30,154 @@ namespace PacketLogger.Models; /// public class NostaleProcesses : IDisposable { - private readonly IDisposable _cleanUp; - private readonly List _errorfulProcesses; + private readonly IDisposable? _cleanUp; + private readonly ManagementEventWatcher? _processStartWatcher; + private readonly ManagementEventWatcher? _processStopWatcher; /// /// Initializes a new instance of the class. /// public NostaleProcesses() { - _errorfulProcesses = new List(); Processes = new ObservableCollection(); - _cleanUp = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)) - .ObserveOn(RxApp.TaskpoolScheduler) - .Subscribe - ( - _ => + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); + if (principal.IsInRole(WindowsBuiltInRole.Administrator)) + { + _cleanUp = Observable.Timer(DateTimeOffset.Now, TimeSpan.FromSeconds(1)) + .Subscribe(_ => UpdateNames()); + + Supported = true; + _processStartWatcher = new ManagementEventWatcher + (new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace")); + _processStartWatcher.EventArrived += HandleProcessOpenedEvent; + _processStartWatcher.Start(); + + _processStopWatcher = new ManagementEventWatcher + (new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace")); + _processStopWatcher.EventArrived += HandleProcessClosedEvent; + _processStopWatcher.Start(); + + // initial nostale processes + // rest is handled by events + foreach (var process in CommsInjector.FindNosTaleProcesses()) { - try - { - Refresh().GetAwaiter().GetResult(); - } - catch (Exception e) - { - Console.WriteLine(e); - } + HandleProcessOpened(process.Id); } - ); + } + } } - /// - /// Gets NosTale processes. - /// - public ObservableCollection Processes { get; } + private void HandleProcessOpenedEvent(object sender, EventArrivedEventArgs e) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + HandleProcessOpened(Convert.ToInt32(e.NewEvent.Properties["ProcessId"].Value)); + } + } - /// - public void Dispose() + private void HandleProcessClosedEvent(object sender, EventArrivedEventArgs e) { - _cleanUp.Dispose(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + HandleProcessClosed(Convert.ToInt32(e.NewEvent.Properties["ProcessId"].Value)); + } } - private async Task Refresh() + private void HandleProcessOpened(int processId) { - var nosTaleProcesses = CommsInjector.FindNosTaleProcesses().Select(x => x.Id).ToArray(); - var toRemove = new List(); + Process process; + try + { + process = Process.GetProcessById(processId); + } + catch (Exception) + { + return; + } - foreach (var currentProcess in Processes) + if (!NosBrowserManager.IsProcessNostaleProcess(process)) { - if (nosTaleProcesses.All(x => x != currentProcess.Process.Id)) - { - toRemove.Add(currentProcess); - } - else - { - RxApp.MainThreadScheduler.Schedule(() => currentProcess.ObserveChanges()); - } + return; } - foreach (var remove in toRemove) + NosBrowserManager nosBrowserManager = new NosBrowserManager + ( + process, + new PlayerManagerOptions(), + new SceneManagerOptions(), + new PetManagerOptions(), + new NetworkManagerOptions(), + new UnitManagerOptions() + ); + var result = nosBrowserManager.Initialize(); + if (!result.IsSuccess) { - RxApp.MainThreadScheduler.Schedule(() => Processes.Remove(remove)); + Console.WriteLine + ($"Got an error when trying to initialize nos browser manager for {process.ProcessName}"); + Console.WriteLine(result.ToFullString()); } - foreach (var openProcess in nosTaleProcesses) + if (nosBrowserManager.IsModuleLoaded()) { - if (Processes.All(x => x.Process.Id != openProcess) && !_errorfulProcesses.Contains(openProcess)) + RxApp.MainThreadScheduler.Schedule + (() => Processes.Add(new NostaleProcess(process, nosBrowserManager))); + } + else + { + Console.WriteLine + ( + $"Cannot add {process.ProcessName} to nostale processes as player manager was not found in memory." + ); + } + } + + private void HandleProcessClosed(int processId) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var process = Processes.FirstOrDefault(x => x.Process.Id == processId); + + if (process is not null) { - var process = Process.GetProcessById(openProcess); - NosBrowserManager nosBrowserManager = new NosBrowserManager - ( - process, - new PlayerManagerOptions(), - new SceneManagerOptions(), - new PetManagerOptions(), - new NetworkManagerOptions(), - new UnitManagerOptions() - ); - var result = nosBrowserManager.Initialize(); - if (result.IsSuccess || (result.Error is CouldNotInitializeModuleError moduleError - && moduleError.Module.Name.Contains("NtClient"))) - { - RxApp.MainThreadScheduler.Schedule - (() => Processes.Add(new NostaleProcess(process, nosBrowserManager))); - } - else - { - _errorfulProcesses.Add(openProcess); - Console.WriteLine(result.ToFullString()); - } + RxApp.MainThreadScheduler.Schedule + (() => Processes.Remove(process)); } } + } - var errorfulToRemove = new List(); + /// + /// Gets whether tracking and attaching to processes is supported for this run. + /// + /// + /// Supported only on Windows run as elevated. + /// + public bool Supported { get; } + + /// + /// Gets NosTale processes. + /// + public ObservableCollection Processes { get; } - foreach (var errorfulProcess in _errorfulProcesses) + /// + public void Dispose() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (nosTaleProcesses.All(x => x != errorfulProcess)) - { - errorfulToRemove.Add(errorfulProcess); - } + _processStartWatcher?.Stop(); + _processStartWatcher?.Dispose(); + _processStopWatcher?.Stop(); + _processStopWatcher?.Dispose(); } + } - foreach (var errorfulRemove in errorfulToRemove) + private void UpdateNames() + { + foreach (var process in Processes) { - _errorfulProcesses.Remove(errorfulRemove); + process.ObserveChanges(); } } } \ No newline at end of file diff --git a/src/PacketLogger/Views/DocumentView.axaml b/src/PacketLogger/Views/DocumentView.axaml index 5c0b7e2..38d58fd 100644 --- a/src/PacketLogger/Views/DocumentView.axaml +++ b/src/PacketLogger/Views/DocumentView.axaml @@ -77,8 +77,11 @@ - + + -- 2.49.0