217 lines
5.8 KiB
C#
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;
|
|
}
|
|
} |