-
-
Notifications
You must be signed in to change notification settings - Fork 344
/
Copy pathProcessHelper.cs
167 lines (148 loc) · 5.41 KB
/
ProcessHelper.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
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Threading;
namespace Flow.Launcher.Plugin.ProcessKiller
{
internal class ProcessHelper
{
private readonly HashSet<string> _systemProcessList = new()
{
"conhost",
"svchost",
"idle",
"system",
"rundll32",
"csrss",
"lsass",
"lsm",
"smss",
"wininit",
"winlogon",
"services",
"spoolsv",
"explorer"
};
private const string FlowLauncherProcessName = "Flow.Launcher";
private bool IsSystemProcessOrFlowLauncher(Process p) =>
_systemProcessList.Contains(p.ProcessName.ToLower()) ||
string.Equals(p.ProcessName, FlowLauncherProcessName, StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Get title based on process name and id
/// </summary>
public static string GetProcessNameIdTitle(Process p)
{
var sb = new StringBuilder();
sb.Append(p.ProcessName);
sb.Append(" - ");
sb.Append(p.Id);
return sb.ToString();
}
/// <summary>
/// Returns a Process for evey running non-system process
/// </summary>
public List<Process> GetMatchingProcesses()
{
var processlist = new List<Process>();
foreach (var p in Process.GetProcesses())
{
if (IsSystemProcessOrFlowLauncher(p)) continue;
processlist.Add(p);
}
return processlist;
}
/// <summary>
/// Returns a dictionary of process IDs and their window titles for processes that have a visible main window with a non-empty title.
/// </summary>
public static unsafe Dictionary<int, string> GetProcessesWithNonEmptyWindowTitle()
{
var processDict = new Dictionary<int, string>();
PInvoke.EnumWindows((hWnd, _) =>
{
var windowTitle = GetWindowTitle(hWnd);
if (!string.IsNullOrWhiteSpace(windowTitle) && PInvoke.IsWindowVisible(hWnd))
{
uint processId = 0;
var result = PInvoke.GetWindowThreadProcessId(hWnd, &processId);
if (result == 0u || processId == 0u)
{
return false;
}
var process = Process.GetProcessById((int)processId);
if (!processDict.ContainsKey((int)processId))
{
processDict.Add((int)processId, windowTitle);
}
}
return true;
}, IntPtr.Zero);
return processDict;
}
private static unsafe string GetWindowTitle(HWND hwnd)
{
var capacity = PInvoke.GetWindowTextLength(hwnd) + 1;
int length;
Span<char> buffer = capacity < 1024 ? stackalloc char[capacity] : new char[capacity];
fixed (char* pBuffer = buffer)
{
// If the window has no title bar or text, if the title bar is empty,
// or if the window or control handle is invalid, the return value is zero.
length = PInvoke.GetWindowText(hwnd, pBuffer, capacity);
}
return buffer[..length].ToString();
}
/// <summary>
/// Returns all non-system processes whose file path matches the given processPath
/// </summary>
public IEnumerable<Process> GetSimilarProcesses(string processPath)
{
return Process.GetProcesses().Where(p => !IsSystemProcessOrFlowLauncher(p) && TryGetProcessFilename(p) == processPath);
}
public void TryKill(PluginInitContext context, Process p)
{
try
{
if (!p.HasExited)
{
p.Kill();
p.WaitForExit(50);
}
}
catch (Exception e)
{
context.API.LogException($"{nameof(ProcessHelper)}", $"Failed to kill process {p.ProcessName}", e);
}
}
public unsafe string TryGetProcessFilename(Process p)
{
try
{
var handle = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION, false, (uint)p.Id);
if (handle.Value == IntPtr.Zero)
{
return string.Empty;
}
using var safeHandle = new SafeProcessHandle(handle.Value, true);
uint capacity = 2000;
Span<char> buffer = new char[capacity];
fixed (char* pBuffer = buffer)
{
if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, (PWSTR)pBuffer, ref capacity))
{
return string.Empty;
}
return buffer[..(int)capacity].ToString();
}
}
catch
{
return string.Empty;
}
}
}
}