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  }