Files
IPClab3/ViewModels/MainWindowViewModel.cs
T
2026-09-19 16:13:20 +03:00

217 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using IPClab3.Models;
using IPClab3.Services;
namespace IPClab3.ViewModels;
public partial class MainWindowViewModel(ProcessHackerService processHackerService, FilePickerService filePickerService) : ObservableObject
{
private ProcessHackerService _processHackerService = processHackerService;
private FilePickerService _filePickerService = filePickerService;
private CancellationTokenSource _cts;
private List<ProcessItem> _allProcesses;
[ObservableProperty]
private ObservableCollection<ProcessItem> _suggestions = [];
private string _searchText = string.Empty;
public string SearchText
{
get => _searchText;
set
{
if (SetProperty(ref _searchText, value))
{
_ = UpdateSuggestionsAsync(value);
}
}
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(AttachProcessCommand))]
public partial ProcessItem SelectedProcess
{
get;
set;
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ReadMemoryCommand))]
[NotifyCanExecuteChangedFor(nameof(WriteMemoryCommand))]
public partial int StartedPid
{
get;
set;
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ReadMemoryCommand))]
[NotifyCanExecuteChangedFor(nameof(WriteMemoryCommand))]
public partial IntPtr MemoryAddress
{
get;
set;
}
private async Task UpdateSuggestionsAsync(string query)
{
// 1. Cancel the previous running search task immediately
_cts?.Cancel();
_cts?.Dispose();
_cts = new CancellationTokenSource();
var token = _cts.Token;
if (string.IsNullOrWhiteSpace(query))
{
Suggestions.Clear();
return;
}
try
{
// Optional: Debounce briefly (e.g., wait 150ms) to ensure the user
// stopped typing before blasting the OS with process queries.
await Task.Delay(150, token);
// 2. Offload the heavy blocking OS operation to a pool thread
List<ProcessItem> filteredResults = await Task.Run(() =>
{
// Check token before starting the heavy lifting
token.ThrowIfCancellationRequested();
// Hit the live OS snapshot
var liveSnapshot = Process.GetProcesses();
token.ThrowIfCancellationRequested();
// Filter on the background thread
return liveSnapshot
.Where(p => p.ProcessName.Contains(query, StringComparison.OrdinalIgnoreCase))
.Select(p => new ProcessItem { Pid = p.Id, ProcessName = p.ProcessName })
.OrderBy(p => p.ProcessName)
.ToList();
}, token);
// 3. Check token one final time before updating the UI thread
token.ThrowIfCancellationRequested();
// Safely swap out items on the Main/UI thread
Suggestions.Clear();
foreach (var process in filteredResults)
{
Suggestions.Add(process);
}
}
catch (OperationCanceledException)
{
// Ignored: Expected when the user types quickly and cancels old operations
}
catch (Exception ex)
{
Debug.WriteLine($"Error fetching processes: {ex.Message}");
}
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(StartProcessCommand))]
public partial string FilePath
{
get;
set;
}
public async Task PickFile(IntPtr hwnd)
{
FilePath = await _filePickerService.RequestFileOpener(hwnd);
}
private bool CanStartProcess()
{
return File.Exists(FilePath);
}
[RelayCommand(CanExecute = nameof(CanStartProcess))]
public void StartProcess()
{
StartedPid = _processHackerService.StartProcess(FilePath);
}
private bool CanAttachProcess()
{
if (SelectedProcess == null) return false;
try
{
var process = Process.GetProcessById(SelectedProcess.Pid);
return true;
}
catch (ArgumentException) { return false; }
}
[RelayCommand(CanExecute = nameof(CanAttachProcess))]
public void AttachProcess()
{
StartedPid = _processHackerService.AttachProcess(SelectedProcess.Pid);
}
[ObservableProperty]
public partial string ReadData
{
get;
set;
} = "Прочитать из памяти";
[ObservableProperty]
public partial int ReadLength
{
get;
set;
}
[ObservableProperty]
public partial string WriteData
{
get;
set;
} = "Записать в память";
private bool CanWorkWithMemory()
{
return StartedPid != 0 && MemoryAddress != IntPtr.Zero;
}
[RelayCommand(CanExecute = nameof(CanWorkWithMemory))]
public void ReadMemory()
{
if (_processHackerService.ReadMemory(MemoryAddress, ReadLength, out var bytes))
ReadData = Encoding.UTF8.GetString(bytes);
}
[RelayCommand(CanExecute = nameof(CanWorkWithMemory))]
public void WriteMemory()
{
if (_processHackerService.WriteMemory(MemoryAddress, WriteData))
{
IsSuccessOpen = true;
Task.Run(() => { Task.Delay(10000); IsSuccessOpen = false; });
}
}
[ObservableProperty]
public partial bool IsSuccessOpen
{
get;
set;
}
}