// 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.Diagnostics; using System.Linq; using System.Text; using Wox.Infrastructure; using Wox.Plugin.Common.Win32; namespace Microsoft.Plugin.WindowWalker.Components { /// /// 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 /// internal class WindowProcess { /// /// Maximum size of a file name /// private const int MaximumFileNameLength = 1000; /// /// An indicator if the window belongs to an 'Universal Windows Platform (UWP)' process /// private readonly bool _isUwpApp; /// /// Gets the id of the process /// internal uint ProcessID { get; private set; } /// /// Gets a value indicating whether the process is responding or not /// internal bool IsResponding { get { try { return Process.GetProcessById((int)ProcessID).Responding; } catch (InvalidOperationException) { // Thrown when process not exist. return true; } catch (NotSupportedException) { // Thrown when process is not running locally. return true; } } } /// /// Gets the id of the thread /// internal uint ThreadID { get; private set; } /// /// Gets the name of the process /// internal string Name { get; private set; } /// /// Gets a value indicating whether the window belongs to an 'Universal Windows Platform (UWP)' process /// internal bool IsUwpApp { get { return _isUwpApp; } } /// /// Gets a value indicating whether this is the shell process or not /// The shell process (like explorer.exe) hosts parts of the user interface (like taskbar, start menu, ...) /// internal bool IsShellProcess { get { IntPtr hShellWindow = NativeMethods.GetShellWindow(); return GetProcessIDFromWindowHandle(hShellWindow) == ProcessID; } } /// /// Gets a value indicating whether the process exists on the machine /// internal bool DoesExist { get { try { var p = Process.GetProcessById((int)ProcessID); p.Dispose(); return true; } catch (InvalidOperationException) { // Thrown when process not exist. return false; } catch (ArgumentException) { // Thrown when process not exist. return false; } } } /// /// Gets a value indicating whether full access to the process is denied or not /// internal bool IsFullAccessDenied { get; private set; } /// /// Initializes a new instance of the class. /// /// New process id. /// New thread id. /// New process name. internal WindowProcess(uint pid, uint tid, string name) { UpdateProcessInfo(pid, tid, name); _isUwpApp = string.Equals(Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase); } /// /// Updates the process information of the instance. /// /// New process id. /// New thread id. /// New process name. internal void UpdateProcessInfo(uint pid, uint tid, string name) { // TODO: Add verification as to whether the process id and thread id is valid ProcessID = pid; ThreadID = tid; Name = name; // Process can be elevated only if process id is not 0 (Dummy value on error) IsFullAccessDenied = (pid != 0) ? TestProcessAccessUsingAllAccessFlag(pid) : false; } /// /// Gets the process ID for the window handle /// /// The handle to the window /// The process ID internal static uint GetProcessIDFromWindowHandle(IntPtr hwnd) { _ = NativeMethods.GetWindowThreadProcessId(hwnd, out uint processId); return processId; } /// /// Gets the thread ID for the window handle /// /// The handle to the window /// The thread ID internal static uint GetThreadIDFromWindowHandle(IntPtr hwnd) { uint threadId = NativeMethods.GetWindowThreadProcessId(hwnd, out _); return threadId; } /// /// Gets the process name for the process ID /// /// The id of the process/param> /// A string representing the process name or an empty string if the function fails internal static string GetProcessNameFromProcessID(uint pid) { IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, true, (int)pid); StringBuilder processName = new StringBuilder(MaximumFileNameLength); if (NativeMethods.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0) { _ = Win32Helpers.CloseHandleIfNotNull(processHandle); return processName.ToString().Split('\\').Reverse().ToArray()[0]; } else { _ = Win32Helpers.CloseHandleIfNotNull(processHandle); return string.Empty; } } /// /// Kills the process by its id. If permissions are required, they will be requested. /// /// Kill process and sub processes. internal void KillThisProcess(bool killProcessTree) { if (IsFullAccessDenied) { string killTree = killProcessTree ? " /t" : string.Empty; Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true); } else { Process.GetProcessById((int)ProcessID).Kill(killProcessTree); } } /// /// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not. /// /// The process ID of the process /// True if denied and false if not. private static bool TestProcessAccessUsingAllAccessFlag(uint pid) { IntPtr processHandle = NativeMethods.OpenProcess(ProcessAccessFlags.AllAccess, true, (int)pid); if (Win32Helpers.GetLastError() == 5) { // Error 5 = ERROR_ACCESS_DENIED _ = Win32Helpers.CloseHandleIfNotNull(processHandle); return true; } else { _ = Win32Helpers.CloseHandleIfNotNull(processHandle); return false; } } } }