-
Notifications
You must be signed in to change notification settings - Fork 6.2k
/
MainViewModel.cs
197 lines (174 loc) · 5.64 KB
/
MainViewModel.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FileLocksmith.Interop;
using ManagedCommon;
namespace PowerToys.FileLocksmithUI.ViewModels
{
#pragma warning disable CA1708 // Identifiers should differ by more than case
public partial class MainViewModel : ObservableObject, IDisposable
#pragma warning restore CA1708 // Identifiers should differ by more than case
{
public IAsyncRelayCommand LoadProcessesCommand { get; }
private bool _isLoading;
private bool _isElevated;
private string[] paths;
private bool _disposed;
private CancellationTokenSource _cancelProcessWatching;
public ObservableCollection<ProcessResult> Processes { get; } = new();
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
OnPropertyChanged(nameof(IsLoading));
}
}
public bool IsElevated
{
get
{
return _isElevated;
}
set
{
_isElevated = value;
OnPropertyChanged(nameof(IsElevated));
}
}
public string[] Paths
{
get => paths;
set
{
paths = value;
OnPropertyChanged(nameof(Paths));
}
}
public string PathsToString
{
get
{
return string.Join("\n", paths);
}
}
public MainViewModel()
{
paths = NativeMethods.ReadPathsFromFile();
Logger.LogInfo($"Starting FileLocksmith with {paths.Length} files selected.");
LoadProcessesCommand = new AsyncRelayCommand(LoadProcessesAsync);
}
private async Task LoadProcessesAsync()
{
IsLoading = true;
Processes.Clear();
if (_cancelProcessWatching is not null)
{
_cancelProcessWatching.Cancel();
}
_cancelProcessWatching = new CancellationTokenSource();
foreach (ProcessResult p in await FindProcesses(paths))
{
Processes.Add(p);
WatchProcess(p, _cancelProcessWatching.Token);
}
IsLoading = false;
}
private async Task<List<ProcessResult>> FindProcesses(string[] paths)
{
var results = new List<ProcessResult>();
await Task.Run(() =>
{
results = NativeMethods.FindProcessesRecursive(paths).ToList();
});
return results;
}
private async void WatchProcess(ProcessResult process, CancellationToken token)
{
try
{
Process handle = Process.GetProcessById((int)process.pid);
try
{
await handle.WaitForExitAsync(token);
}
catch (TaskCanceledException)
{
// Nothing to do, normal operation
}
if (handle.HasExited)
{
Processes.Remove(process);
}
}
catch (Exception ex)
{
Logger.LogError($"Couldn't add a waiter to wait for a process to exit. PID = {process.pid} and Name = {process.name}.", ex);
Processes.Remove(process); // If we couldn't get an handle to the process or it has exited in the meanwhile, don't show it.
}
}
[RelayCommand]
public void EndTask(ProcessResult selectedProcess)
{
try
{
Process handle = Process.GetProcessById((int)selectedProcess.pid);
try
{
handle.Kill();
}
catch (Exception ex)
{
Logger.LogError($"Couldn't kill process {selectedProcess.name} with PID {selectedProcess.pid}.", ex);
}
}
catch (Exception ex)
{
Logger.LogError($"Couldn't get an handle to kill process {selectedProcess.name} with PID {selectedProcess.pid}. Likely has been killed already.", ex);
Processes.Remove(selectedProcess); // If we couldn't get an handle to the process, remove it from the list, since it's likely been killed already.
}
}
[RelayCommand]
public void RestartElevated()
{
if (NativeMethods.StartAsElevated(paths))
{
// TODO gentler exit
Environment.Exit(0);
}
else
{
// TODO report error?
Logger.LogError($"Couldn't restart as elevated.");
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_disposed = true;
}
}
}
}
}