/ src / modules / launcher / Plugins / Microsoft.Plugin.WindowWalker / Components / WindowProcess.cs
WindowProcess.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 using System; 5 using System.Diagnostics; 6 using System.Linq; 7 using System.Text; 8 9 using Wox.Infrastructure; 10 using Wox.Plugin.Common.Win32; 11 12 namespace Microsoft.Plugin.WindowWalker.Components 13 { 14 /// <summary> 15 /// Represents the process data of an open window. This class is used in the process cache and for the process object of the open window 16 /// </summary> 17 internal class WindowProcess 18 { 19 /// <summary> 20 /// Maximum size of a file name 21 /// </summary> 22 private const int MaximumFileNameLength = 1000; 23 24 /// <summary> 25 /// An indicator if the window belongs to an 'Universal Windows Platform (UWP)' process 26 /// </summary> 27 private readonly bool _isUwpApp; 28 29 /// <summary> 30 /// Gets the id of the process 31 /// </summary> 32 internal uint ProcessID 33 { 34 get; private set; 35 } 36 37 /// <summary> 38 /// Gets a value indicating whether the process is responding or not 39 /// </summary> 40 internal bool IsResponding 41 { 42 get 43 { 44 try 45 { 46 return Process.GetProcessById((int)ProcessID).Responding; 47 } 48 catch (InvalidOperationException) 49 { 50 // Thrown when process not exist. 51 return true; 52 } 53 catch (NotSupportedException) 54 { 55 // Thrown when process is not running locally. 56 return true; 57 } 58 } 59 } 60 61 /// <summary> 62 /// Gets the id of the thread 63 /// </summary> 64 internal uint ThreadID 65 { 66 get; private set; 67 } 68 69 /// <summary> 70 /// Gets the name of the process 71 /// </summary> 72 internal string Name 73 { 74 get; private set; 75 } 76 77 /// <summary> 78 /// Gets a value indicating whether the window belongs to an 'Universal Windows Platform (UWP)' process 79 /// </summary> 80 internal bool IsUwpApp 81 { 82 get { return _isUwpApp; } 83 } 84 85 /// <summary> 86 /// Gets a value indicating whether this is the shell process or not 87 /// The shell process (like explorer.exe) hosts parts of the user interface (like taskbar, start menu, ...) 88 /// </summary> 89 internal bool IsShellProcess 90 { 91 get 92 { 93 IntPtr hShellWindow = NativeMethods.GetShellWindow(); 94 return GetProcessIDFromWindowHandle(hShellWindow) == ProcessID; 95 } 96 } 97 98 /// <summary> 99 /// Gets a value indicating whether the process exists on the machine 100 /// </summary> 101 internal bool DoesExist 102 { 103 get 104 { 105 try 106 { 107 var p = Process.GetProcessById((int)ProcessID); 108 p.Dispose(); 109 return true; 110 } 111 catch (InvalidOperationException) 112 { 113 // Thrown when process not exist. 114 return false; 115 } 116 catch (ArgumentException) 117 { 118 // Thrown when process not exist. 119 return false; 120 } 121 } 122 } 123 124 /// <summary> 125 /// Gets a value indicating whether full access to the process is denied or not 126 /// </summary> 127 internal bool IsFullAccessDenied 128 { 129 get; private set; 130 } 131 132 /// <summary> 133 /// Initializes a new instance of the <see cref="WindowProcess"/> class. 134 /// </summary> 135 /// <param name="pid">New process id.</param> 136 /// <param name="tid">New thread id.</param> 137 /// <param name="name">New process name.</param> 138 internal WindowProcess(uint pid, uint tid, string name) 139 { 140 UpdateProcessInfo(pid, tid, name); 141 _isUwpApp = string.Equals(Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase); 142 } 143 144 /// <summary> 145 /// Updates the process information of the <see cref="WindowProcess"/> instance. 146 /// </summary> 147 /// <param name="pid">New process id.</param> 148 /// <param name="tid">New thread id.</param> 149 /// <param name="name">New process name.</param> 150 internal void UpdateProcessInfo(uint pid, uint tid, string name) 151 { 152 // TODO: Add verification as to whether the process id and thread id is valid 153 ProcessID = pid; 154 ThreadID = tid; 155 Name = name; 156 157 // Process can be elevated only if process id is not 0 (Dummy value on error) 158 IsFullAccessDenied = (pid != 0) ? TestProcessAccessUsingAllAccessFlag(pid) : false; 159 } 160 161 /// <summary> 162 /// Gets the process ID for the window handle 163 /// </summary> 164 /// <param name="hwnd">The handle to the window</param> 165 /// <returns>The process ID</returns> 166 internal static uint GetProcessIDFromWindowHandle(IntPtr hwnd) 167 { 168 _ = NativeMethods.GetWindowThreadProcessId(hwnd, out uint processId); 169 return processId; 170 } 171 172 /// <summary> 173 /// Gets the thread ID for the window handle 174 /// </summary> 175 /// <param name="hwnd">The handle to the window</param> 176 /// <returns>The thread ID</returns> 177 internal static uint GetThreadIDFromWindowHandle(IntPtr hwnd) 178 { 179 uint threadId = NativeMethods.GetWindowThreadProcessId(hwnd, out _); 180 return threadId; 181 } 182 183 /// <summary> 184 /// Gets the process name for the process ID 185 /// </summary> 186 /// <param name="pid">The id of the process/param> 187 /// <returns>A string representing the process name or an empty string if the function fails</returns> 188 internal static string GetProcessNameFromProcessID(uint pid) 189 { 190 IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, true, (int)pid); 191 StringBuilder processName = new StringBuilder(MaximumFileNameLength); 192 193 if (NativeMethods.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0) 194 { 195 _ = Win32Helpers.CloseHandleIfNotNull(processHandle); 196 return processName.ToString().Split('\\').Reverse().ToArray()[0]; 197 } 198 else 199 { 200 _ = Win32Helpers.CloseHandleIfNotNull(processHandle); 201 return string.Empty; 202 } 203 } 204 205 /// <summary> 206 /// Kills the process by its id. If permissions are required, they will be requested. 207 /// </summary> 208 /// <param name="killProcessTree">Kill process and sub processes.</param> 209 internal void KillThisProcess(bool killProcessTree) 210 { 211 if (IsFullAccessDenied) 212 { 213 string killTree = killProcessTree ? " /t" : string.Empty; 214 Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true); 215 } 216 else 217 { 218 Process.GetProcessById((int)ProcessID).Kill(killProcessTree); 219 } 220 } 221 222 /// <summary> 223 /// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not. 224 /// </summary> 225 /// <param name="pid">The process ID of the process</param> 226 /// <returns>True if denied and false if not.</returns> 227 private static bool TestProcessAccessUsingAllAccessFlag(uint pid) 228 { 229 IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.AllAccess, true, (int)pid); 230 231 if (Win32Helpers.GetLastError() == 5) 232 { 233 // Error 5 = ERROR_ACCESS_DENIED 234 _ = Win32Helpers.CloseHandleIfNotNull(processHandle); 235 return true; 236 } 237 else 238 { 239 _ = Win32Helpers.CloseHandleIfNotNull(processHandle); 240 return false; 241 } 242 } 243 } 244 }