Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions Process-Explorer.BLL/HostedServices/ProcessMetricsHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

namespace Process_Explorer.BLL.HostedServices
{
public class ProcessMetricsHostedService : IHostedService
public class ProcessMetricsHostedService : BackgroundService
{
private readonly ILogger<ProcessMetricsHostedService> _logger = default!;
private readonly IProcessService _service = default!;
private readonly ILogger<ProcessMetricsHostedService> _logger;
private readonly IProcessService _service;
private Timer _timer = default!;

public List<ProcessInformationDTO> Processes { get; private set; } = new();
public event Action<IReadOnlyList<ProcessInformationDTO>>? ProcessesUpdated;

public ProcessMetricsHostedService(IProcessService service, ILogger<ProcessMetricsHostedService> logger)
{
Expand All @@ -23,7 +23,9 @@ private async void UpdateProcesses(object? state)
{
try
{
Processes = (await _service.GetProcessesInformationAsync()).ToList();
var processes = (await _service.GetProcessesInformationAsync())!.ToList()!;

ProcessesUpdated?.Invoke(processes!);

_logger.LogInformation("Process list updated successfully.");
}
Expand All @@ -33,16 +35,17 @@ private async void UpdateProcesses(object? state)
}
}

public Task StartAsync(CancellationToken cancellationToken)
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(UpdateProcesses, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
public override Task StopAsync(CancellationToken cancellationToken)
{
_timer?.DisposeAsync();
_timer?.Dispose();
return Task.CompletedTask;
}
}

}
3 changes: 3 additions & 0 deletions Process-Explorer.BLL/Models/ProcessInformationDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
public class ProcessInformationDTO
{
public uint PID { get; set; }
public uint PPID { get; set; }
public uint HandlesCount { get; set; }
public uint ThreadsCount { get; set; }
public string Name { get; set; } = string.Empty;
public string Company { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
Expand Down
3 changes: 3 additions & 0 deletions Process-Explorer.BLL/Profiles/ProcessInforamtionProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public ProcessInforamtionProfile()
{
CreateMap<ProcessInformation, ProcessInformationDTO>();
CreateMap<ProcessInformationDTO, ProcessInformation>();

CreateMap<ProcessInformationEx, ProcessInformationDTO>();
CreateMap<ProcessInformationDTO, ProcessInformationEx>();
}
}
}
3 changes: 2 additions & 1 deletion Process-Explorer.BLL/Services/IProcessService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ public interface IProcessService
public Task<IEnumerable<Process?>> GetActiveProcessesAsync();
public Task<Process?> GetProcessByIdAsync(int pid);
public Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync();
public Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync(IEnumerable<Process> processes);
public Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync(IEnumerable<ProcessEx> processes);

public Task<ProcessInformationDTO?> GetProcessInformationByIdAsync(int pid);
public Task KillProcess(int pid);
}
Expand Down
9 changes: 6 additions & 3 deletions Process-Explorer.BLL/Services/ProcessService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ public ProcessService(IMapper mapper, ILogger<ProcessService> logger)
public async Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync()
{
_logger.LogDebug("GetProcessesInformation called");

return await GetProcessesInformationAsync(await ProcessManager.GetActiveProcessesAsync());
if (false)
{
//return await GetProcessesInformationAsync(await Native.ProcessManager.GetActiveProcessesAsync());
}
return await GetProcessesInformationAsync(await Native.ProcessManager.NtGetActiveProcessesAsync());
}

public Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync(IEnumerable<Process> processes)
public Task<IEnumerable<ProcessInformationDTO?>?> GetProcessesInformationAsync(IEnumerable<ProcessEx> processes)
{
_logger.LogDebug("GetProcessesInformation(procesess) called");
try
Expand Down
60 changes: 33 additions & 27 deletions Process-Explorer.GUI/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml;
using Process_Explorer.BLL.HostedServices;
using Process_Explorer.GUI.Extensions;
using Process_Explorer.GUI.Helpers;
using System;

namespace Process_Explorer.GUI
{
public partial class App : Application
{
private IServiceProvider _services = default!;
private IHost _host = default!;
private Window _window = default!;

public App()
{

InitializeComponent();
Setup();
SetupApplication();
SetupHost();
}

private void Setup()
private void SetupHost()
{
_host = Host.CreateDefaultBuilder()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureServices((ctx, services) => services.ConfigureServices())
.Build();
}

private void SetupApplication()
{
try
{
Expand All @@ -28,32 +42,24 @@ private void Setup()
}
catch (Exception ex)
{
Native.MessageBox.ShowWarning(ex.Message);
ToastNotificationHelper.ShowMessage("Process Explorer Message", ex.Message);
}
}

var services = new ServiceCollection();

services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
});
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
await _host.StartAsync();

services.ConfigureServices();
_window = _host.Services.GetRequiredService<MainWindow>();
_window.Activate();

_services = services.BuildServiceProvider();
_window.Closed += OnMainWindowClosed;
}

protected override void OnLaunched(LaunchActivatedEventArgs args)
private async void OnMainWindowClosed(object? sender, WindowEventArgs e)
{
m_window = _services.GetRequiredService<MainWindow>();
m_window.Activate();

var hostedService = _services.GetRequiredService<ProcessMetricsHostedService>();
hostedService.StartAsync(CancellationToken.None);
await _host.StopAsync(TimeSpan.FromSeconds(5));
_host.Dispose();
}

private Window? m_window;
}
}
14 changes: 14 additions & 0 deletions Process-Explorer.GUI/Helpers/IconHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,19 @@ public static async Task<BitmapImage> GetIconAsync(string exePath)
return null!;
}
}
public static async Task<BitmapImage> GetDefaultIcon()
{
using (var bitmap = SystemIcons.Application.ToBitmap())
{
var bitmapImage = new Microsoft.UI.Xaml.Media.Imaging.BitmapImage();
using (var memoryStream = new System.IO.MemoryStream())
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
await bitmapImage.SetSourceAsync(memoryStream.AsRandomAccessStream());
}
return bitmapImage;
}
}
}
}
18 changes: 18 additions & 0 deletions Process-Explorer.GUI/Helpers/ToastNotificationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;

namespace Process_Explorer.GUI.Helpers
{
public static class ToastNotificationHelper
{
public static void ShowMessage(string title, string message)
{
var toast = new AppNotificationBuilder()
.AddText(title)
.AddText(message)
.BuildNotification();

AppNotificationManager.Default.Show(toast);
}
}
}
2 changes: 2 additions & 0 deletions Process-Explorer.GUI/Process-Explorer.GUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CSharpMarkup.WinUI.LiveChartsCore.SkiaSharpView" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.5" />
Expand Down
75 changes: 39 additions & 36 deletions Process-Explorer.GUI/ViewModels/ActionsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,19 @@
using Process_Explorer.GUI.Helpers;
using Process_Explorer.GUI.Models;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Process_Explorer.GUI.ViewModels
{
public partial class ActionsViewModel : ObservableObject, IDisposable
public partial class ActionsViewModel : ObservableObject
{
private readonly ProcessMetricsHostedService _service = default!;
private readonly Timer _timer = default!;

private readonly DispatcherQueue _dispatcher = default!;

public MemoryUsageChart CpuChart;
public MemoryUsageChart PrivateChart;
public MemoryUsageChart WorkingChart;
public MemoryUsageChart CpuChart = default!;
public MemoryUsageChart PrivateChart = default!;
public MemoryUsageChart WorkingChart = default!;

[ObservableProperty]
private Visibility _progressBarVisiblity;
Expand All @@ -33,14 +30,19 @@ public partial class ActionsViewModel : ObservableObject, IDisposable
[ObservableProperty]
private bool _isLoading = true;

private int _targetProcessId;
[ObservableProperty]
private string _text = "Enter Process Id [PID]";

private int _targetProcessId = -1;
public int TargetProcessId
{
get => _targetProcessId;
set
{
if (SetProperty(ref _targetProcessId, value))
{
Text = (value == 0) ? "Idle Process [Information Cannot Be Obtained]" : "Enter Process Id [PID]";

IsLoading = true;
ProcessAddress = "Loading...";

Expand Down Expand Up @@ -80,37 +82,35 @@ public ActionsViewModel(ProcessMetricsHostedService service)

_service = service;
_dispatcher = DispatcherQueue.GetForCurrentThread();
_timer = new Timer(UpdateMetrics, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));




KillCommand = new RelayCommand(OnKillButtonClicked);

_service.ProcessesUpdated += OnProcessesUpdated;
}

private void UpdateMetrics(object? state)
private void OnProcessesUpdated(IReadOnlyList<ProcessInformationDTO> processes)
{
if (_dispatcher is not null)
_dispatcher.TryEnqueue(() =>
{
_dispatcher.TryEnqueue(() =>
{

if (_service.Processes.FirstOrDefault(p => p.PID == TargetProcessId) is not null)
{
_model = _service.Processes.FirstOrDefault(p => p.PID == TargetProcessId)!;

CpuChart.AddValue(_model.CpuUsage);
PrivateChart.AddValue(_model.PrivateBytes);
WorkingChart.AddValue(_model.WorkingSet);

ProcessAddress = _model.Name;
IsLoading = false;
}
else
{
IsLoading = true;
ProcessAddress = "NULL";
}
});
}
var process = processes.FirstOrDefault(p => p.PID == TargetProcessId);

if (process is not null)
{
_model = process;
CpuChart.AddValue(_model.CpuUsage);
PrivateChart.AddValue(_model.PrivateBytes);
WorkingChart.AddValue(_model.WorkingSet);

ProcessAddress = _model.Name;
IsLoading = false;
}
else
{
IsLoading = true;
ProcessAddress = "NULL";
}
});
}

public void OnKillButtonClicked()
Expand All @@ -121,6 +121,9 @@ public void OnKillButtonClicked()
}
}

public void Dispose() => _timer?.Dispose();
public void Dispose()
{
_service.ProcessesUpdated -= OnProcessesUpdated;
}
}
}
Loading