[PT Run] WindowWalker: Refactor code, fix some bugs, hide UWP non-windows, prepare code for new features (#15441)

* Import files from old PR #15329

* Improvements

* hide uwp non-windows (#13637)

* update debug tool tip

* fix spelling and comments

* disable tool tip

* fix doc links

* remove obsolete using

* Update docs

* fix spelling

* rename elevation property and test method

* Add property <DoesExist> to WindowProcess class

* Close process handles correctly if not used anymore

* cleanup coed

* fix bug with sticky notes process

* add window class to tool tip

* small change

* make nativeMethods static class

* fix broken uwpApp property of WindowProcess class

* rename method

* Revert making NativeMethods class static. It contains instance members.

* improve loggign

* fix merge mistakes

* fixes

* remove obsolete delegate

* Improve SearchController to speed up search (#15561)

* add <IsShellProcess> property to <WindowProcess> class

* reorder code

* disable debug tool tip

* Update devdocs

* remove obsolete event handler

* update var name
This commit is contained in:
Heiko
2022-01-25 10:33:40 +01:00
committed by GitHub
parent 5eaf60e8a2
commit edc43e39ca
8 changed files with 429 additions and 156 deletions

View File

@@ -723,6 +723,7 @@ HOLDESC
homepage
homljgmgpmcbpjbnjpfijnhipfkiclkd
HOOKPROC
Hostbackdropbrush
hostname
hotkeycontrol
hotkeys

View File

@@ -3,16 +3,19 @@ The window walker plugin matches the user entered query with the open windows on
![Image of Window Walker plugin](/doc/images/launcher/plugins/windowwalker.png)
### [`OpenWindows.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs)
- The window walker plugin uses the `EnumWindows` function to enumerate all the open windows in the [`OpenWindows.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs) class.
### [`OpenWindows.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs)
- The window walker plugin uses the `EnumWindows` function to enumerate all the open windows in the [`OpenWindows.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs) class.
### [`SearchController.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs)
- The [`SearchController`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs) encapsulates the functions needed to search and find matches.
- It is responsible for updating the search text and performing a fuzzy search on all the open windows.
### [`SearchController.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs)
- The [`SearchController`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs) encapsulates the functions needed to search and find matches.
- It is responsible for updating the search text and performing a fuzzy search on all the open windows in an asynchronous manner.
### [`Window.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs)
- The [`Window`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs) class represents a specific window and has functions to get the name of the window, the state of the window (whether it is visible or not), and the `SwitchTowindow` function which switches the desktop focus to the selected window. This action is performed when the user clicks on a window walker plugin result.
- The `Window` class holds a static cache with the process information of all windows we know so far and each window instance has a property which holds its process information (name, file, ...). The process data in the cache and the window property are of the type `WindowProcess`.
### [`Window.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs)
- The [`Window`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs) class represents a specific window and has functions to get the name of the process, the state of the window (whether it is visible or not), and the `SwitchTowindow` function which switches the desktop focus to the selected window. This action is performed when the user clicks on a window walker plugin result.
### [`WindowProcess.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs)
- The [`WindowProcess`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs) class represents a specific process for a window. It contains static methods to query process information from the system. And it contains instance methods and properties to hold/retrieve the process information we want to know about a window's process.
### Score
The window walker plugin uses [`FuzzyMatching`](src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/FuzzyMatching.cs) to get the matching indices and calculates the score by creating a 2 dimensional array of the window and the query text.
The window walker plugin uses [`FuzzyMatching`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/FuzzyMatching.cs) to get the matching indices and calculates the score by creating a 2 dimensional array of the window and the query text.

View File

@@ -249,7 +249,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
}
/// <summary>
/// Window attribute
/// DWM window attribute (Windows 7 and earlier: The values between ExcludedFromPeek and Last aren't supported.)
/// </summary>
[Flags]
public enum DwmWindowAttribute
@@ -266,9 +266,32 @@ namespace Microsoft.Plugin.WindowWalker.Components
HasIconicBitmap,
DisallowPeek,
ExcludedFromPeek,
Cloak,
Cloaked,
FreezeRepresentation,
PassiveUpdateMode,
UseHostbackdropbrush,
UseImmersiveDarkMode,
WindowCornerPreference,
BorderColor,
CaptionColor,
TextColor,
VisibleFrameBorderThickness,
Last,
}
/// <summary>
/// Flags for describing the window cloak state (Windows 7 and earlier: This value is not supported.)
/// </summary>
[Flags]
public enum DwmWindowCloakState
{
None = 0,
CloakedApp = 1,
CloakedShell = 2,
CloakedInherited = 4,
}
/// <summary>
/// Flags for accessing the process in trying to get icon for the process
/// </summary>
@@ -869,7 +892,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
[DllImport("user32.dll", SetLastError = true, BestFitMapping = false)]
public static extern IntPtr GetProp(IntPtr hWnd, string lpString);
[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
[DllImport("dwmapi.dll", EntryPoint = "#113", CallingConvention = CallingConvention.StdCall)]
@@ -890,5 +913,37 @@ namespace Microsoft.Plugin.WindowWalker.Components
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("user32.dll")]
public static extern IntPtr GetShellWindow();
/// <summary>
/// Returns the last Win32 Error code thrown by a native method if enabled for this method.
/// </summary>
/// <returns>The error code as int value.</returns>
public static int GetLastWin32Error()
{
return Marshal.GetLastWin32Error();
}
/// <summary>
/// Validate that the handle is not null and close it.
/// </summary>
/// <param name="handle">Handle to close.</param>
/// <returns>Zero if native method fails and nonzero if the native method succeeds.</returns>
public static bool CloseHandleIfNotNull(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
// Return true if there is nothing to close.
return true;
}
return CloseHandle(handle);
}
}
}

View File

@@ -20,11 +20,6 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// </summary>
private static readonly string _powerLauncherExe = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
/// <summary>
/// Delegate handler for open windows updates
/// </summary>
public delegate void OpenWindowsUpdateEventHandler(object sender, SearchController.SearchResultUpdateEventArgs e);
/// <summary>
/// List of all the open windows
/// </summary>
@@ -93,9 +88,14 @@ namespace Microsoft.Plugin.WindowWalker.Components
if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
(!newWindow.IsToolWindow || newWindow.IsAppWindow) && !newWindow.TaskListDeleted &&
newWindow.ClassName != "Windows.UI.Core.CoreWindow" && newWindow.ProcessName != _powerLauncherExe)
newWindow.ClassName != "Windows.UI.Core.CoreWindow" && newWindow.ProcessInfo.Name != _powerLauncherExe)
{
windows.Add(newWindow);
// To hide (not add) preloaded uwp app windows that are invisible to the user we check the cloak state in DWM to be "none". (Issue #13637.)
// (If user asking to see these windows again we can add an optional plugin setting in the future.)
if (!newWindow.IsCloaked)
{
windows.Add(newWindow);
}
}
return true;

View File

@@ -7,7 +7,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.Plugin.WindowWalker.Components
{
@@ -24,7 +23,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// <summary>
/// Open window search results
/// </summary
/// </summary>
private List<SearchResult> searchMatches;
/// <summary>
@@ -32,16 +31,6 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// </summary>
private static SearchController instance;
/// <summary>
/// Delegate handler for open windows updates
/// </summary>
public delegate void SearchResultUpdateEventHandler(object sender, SearchResultUpdateEventArgs e);
/// <summary>
/// Event raised when there is an update to the list of open windows
/// </summary>
public event SearchResultUpdateEventHandler OnSearchResultUpdateEventHandler;
/// <summary>
/// Gets or sets the current search text
/// </summary>
@@ -95,16 +84,16 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// <summary>
/// Event handler for when the search text has been updated
/// </summary>
public async Task UpdateSearchText(string searchText)
public void UpdateSearchText(string searchText)
{
SearchText = searchText;
await SyncOpenWindowsWithModelAsync().ConfigureAwait(false);
SyncOpenWindowsWithModel();
}
/// <summary>
/// Syncs the open windows with the OpenWindows Model
/// </summary>
public async Task SyncOpenWindowsWithModelAsync()
public void SyncOpenWindowsWithModel()
{
System.Diagnostics.Debug.Print("Syncing WindowSearch result with OpenWindows Model");
@@ -116,22 +105,8 @@ namespace Microsoft.Plugin.WindowWalker.Components
}
else
{
searchMatches = await FuzzySearchOpenWindowsAsync(snapshotOfOpenWindows).ConfigureAwait(false);
searchMatches = FuzzySearchOpenWindows(snapshotOfOpenWindows);
}
OnSearchResultUpdateEventHandler?.Invoke(this, new SearchResultUpdateEventArgs());
}
/// <summary>
/// Redirecting method for Fuzzy searching
/// </summary>
/// <param name="openWindows">what windows are open</param>
/// <returns>Returns search results</returns>
private Task<List<SearchResult>> FuzzySearchOpenWindowsAsync(List<Window> openWindows)
{
return Task.Run(
() =>
FuzzySearchOpenWindows(openWindows));
}
/// <summary>
@@ -151,7 +126,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
foreach (var window in openWindows)
{
var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchString.SearchText);
var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.ProcessName, searchString.SearchText);
var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.ProcessInfo.Name, searchString.SearchText);
if ((titleMatch.Count != 0 || processMatch.Count != 0) &&
window.Title.Length != 0)

View File

@@ -7,9 +7,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wox.Plugin.Logger;
namespace Microsoft.Plugin.WindowWalker.Components
{
@@ -18,22 +18,22 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// </summary>
public class Window
{
/// <summary>
/// Maximum size of a file name
/// </summary>
private const int MaximumFileNameLength = 1000;
/// <summary>
/// The list of owners of a window so that we don't have to
/// constantly query for the process owning a specific window
/// </summary>
private static readonly Dictionary<IntPtr, string> _handlesToProcessCache = new Dictionary<IntPtr, string>();
/// <summary>
/// The handle to the window
/// </summary>
private readonly IntPtr hwnd;
/// <summary>
/// A static cache for the process data of all known windows
/// that we don't have to query the data every time
/// </summary>
private static readonly Dictionary<IntPtr, WindowProcess> _handlesToProcessCache = new Dictionary<IntPtr, WindowProcess>();
/// <summary>
/// An instance of <see cref="WindowProcess"/> that contains the process information for the window
/// </summary>
private readonly WindowProcess processInfo;
/// <summary>
/// Gets the title of the window (the string displayed at the top of the window)
/// </summary>
@@ -68,63 +68,12 @@ namespace Microsoft.Plugin.WindowWalker.Components
get { return hwnd; }
}
public uint ProcessID { get; set; }
/// <summary>
/// Gets the name of the process
/// Gets the object of with the process information of the window
/// </summary>
public string ProcessName
public WindowProcess ProcessInfo
{
get
{
lock (_handlesToProcessCache)
{
if (_handlesToProcessCache.Count > 7000)
{
Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
_handlesToProcessCache.Clear();
}
if (!_handlesToProcessCache.ContainsKey(Hwnd))
{
var processName = GetProcessNameFromWindowHandle(Hwnd);
if (processName.Length != 0)
{
_handlesToProcessCache.Add(
Hwnd,
processName.ToString().Split('\\').Reverse().ToArray()[0]);
}
else
{
_handlesToProcessCache.Add(Hwnd, string.Empty);
}
}
if (_handlesToProcessCache[hwnd].ToUpperInvariant() == "APPLICATIONFRAMEHOST.EXE")
{
new Task(() =>
{
NativeMethods.CallBackPtr callbackptr = new NativeMethods.CallBackPtr((IntPtr hwnd, IntPtr lParam) =>
{
var childProcessId = GetProcessIDFromWindowHandle(hwnd);
if (childProcessId != ProcessID)
{
_handlesToProcessCache[Hwnd] = GetProcessNameFromWindowHandle(hwnd);
return false;
}
else
{
return true;
}
});
_ = NativeMethods.EnumChildWindows(Hwnd, callbackptr, 0);
}).Start();
}
return _handlesToProcessCache[hwnd];
}
}
get { return processInfo; }
}
/// <summary>
@@ -134,15 +83,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
{
get
{
StringBuilder windowClassName = new StringBuilder(300);
var numCharactersWritten = NativeMethods.GetClassName(Hwnd, windowClassName, windowClassName.MaxCapacity);
if (numCharactersWritten == 0)
{
return string.Empty;
}
return windowClassName.ToString();
return GetWindowClassName(Hwnd);
}
}
@@ -157,6 +98,18 @@ namespace Microsoft.Plugin.WindowWalker.Components
}
}
/// <summary>
/// Gets a value indicating whether the window is cloaked (true) or not (false).
/// (A cloaked window is not visible to the user. But the window is still composed by DWM.)
/// </summary>
public bool IsCloaked
{
get
{
return GetWindowCloakState() != WindowCloakState.None;
}
}
/// <summary>
/// Gets a value indicating whether the specified window handle identifies an existing window.
/// </summary>
@@ -236,6 +189,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
{
// TODO: Add verification as to whether the window handle is valid
this.hwnd = hwnd;
processInfo = CreateWindowProcessInstance(hwnd);
}
/// <summary>
@@ -248,7 +202,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
// to use ShowWindow for switching tabs in IE
// 2) SetForegroundWindow fails on minimized windows
// Using Ordinal since this is internal
if (ProcessName.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) || !Minimized)
if (processInfo.Name.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) || !Minimized)
{
NativeMethods.SetForegroundWindow(Hwnd);
}
@@ -271,7 +225,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
public override string ToString()
{
// Using CurrentCulture since this is user facing
return Title + " (" + ProcessName.ToUpper(CultureInfo.CurrentCulture) + ")";
return Title + " (" + processInfo.Name.ToUpper(CultureInfo.CurrentCulture) + ")";
}
/// <summary>
@@ -309,36 +263,125 @@ namespace Microsoft.Plugin.WindowWalker.Components
}
/// <summary>
/// Gets the name of the process using the window handle
/// Returns the window cloak state from DWM
/// (A cloaked window is not visible to the user. But the window is still composed by DWM.)
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>A string representing the process name or an empty string if the function fails</returns>
private string GetProcessNameFromWindowHandle(IntPtr hwnd)
/// <returns>The state (none, app, ...) of the window</returns>
public WindowCloakState GetWindowCloakState()
{
uint processId = GetProcessIDFromWindowHandle(hwnd);
ProcessID = processId;
IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, true, (int)processId);
StringBuilder processName = new StringBuilder(MaximumFileNameLength);
_ = NativeMethods.DwmGetWindowAttribute(Hwnd, (int)NativeMethods.DwmWindowAttribute.Cloaked, out int isCloakedState, sizeof(uint));
if (NativeMethods.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0)
switch (isCloakedState)
{
return processName.ToString().Split('\\').Reverse().ToArray()[0];
}
else
{
return string.Empty;
case (int)NativeMethods.DwmWindowCloakState.None:
return WindowCloakState.None;
case (int)NativeMethods.DwmWindowCloakState.CloakedApp:
return WindowCloakState.App;
case (int)NativeMethods.DwmWindowCloakState.CloakedShell:
return WindowCloakState.Shell;
case (int)NativeMethods.DwmWindowCloakState.CloakedInherited:
return WindowCloakState.Inherited;
default:
return WindowCloakState.Unknown;
}
}
/// <summary>
/// Gets the process ID for the Window handle
/// Enum to simplify the cloak state of the window
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The process ID</returns>
private static uint GetProcessIDFromWindowHandle(IntPtr hwnd)
public enum WindowCloakState
{
_ = NativeMethods.GetWindowThreadProcessId(hwnd, out uint processId);
return processId;
None,
App,
Shell,
Inherited,
Unknown,
}
/// <summary>
/// Returns the class name of a window.
/// </summary>
/// <param name="hwnd">Handle to the window.</param>
/// <returns>Class name</returns>
private static string GetWindowClassName(IntPtr hwnd)
{
StringBuilder windowClassName = new StringBuilder(300);
var numCharactersWritten = NativeMethods.GetClassName(hwnd, windowClassName, windowClassName.MaxCapacity);
if (numCharactersWritten == 0)
{
return string.Empty;
}
return windowClassName.ToString();
}
/// <summary>
/// Gets an instance of <see cref="WindowProcess"/> form process cache or creates a new one. A new one will be added to the cache.
/// </summary>
/// <param name="hWindow">The handle to the window</param>
/// <returns>A new Instance of type <see cref="WindowProcess"/></returns>
private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
{
lock (_handlesToProcessCache)
{
if (_handlesToProcessCache.Count > 7000)
{
Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
_handlesToProcessCache.Clear();
}
// Add window's process to cache if missing
if (!_handlesToProcessCache.ContainsKey(hWindow))
{
// Get process ID and name
var processId = WindowProcess.GetProcessIDFromWindowHandle(hWindow);
var threadId = WindowProcess.GetThreadIDFromWindowHandle(hWindow);
var processName = WindowProcess.GetProcessNameFromProcessID(processId);
if (processName.Length != 0)
{
_handlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
}
else
{
// For the dwm process we can not receive the name. This is no problem because the window isn't part of result list.
Log.Debug($"Invalid process {processId} ({processName}) for window handle {hWindow}.", typeof(Window));
_handlesToProcessCache.Add(hWindow, new WindowProcess(0, 0, string.Empty));
}
}
// Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe'
// (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.)
if (_handlesToProcessCache[hWindow].Name.ToUpperInvariant() == "APPLICATIONFRAMEHOST.EXE")
{
new Task(() =>
{
NativeMethods.CallBackPtr callbackptr = new NativeMethods.CallBackPtr((IntPtr hwnd, IntPtr lParam) =>
{
// Every uwp app main window has at least three child windows. Only the one we are interested in has a class starting with "Windows.UI.Core." and is assigned to the real app process.
// (The other ones have a class name that begins with the string "ApplicationFrame".)
if (GetWindowClassName(hwnd).StartsWith("Windows.UI.Core.", StringComparison.OrdinalIgnoreCase))
{
var childProcessId = WindowProcess.GetProcessIDFromWindowHandle(hwnd);
var childThreadId = WindowProcess.GetThreadIDFromWindowHandle(hwnd);
var childProcessName = WindowProcess.GetProcessNameFromProcessID(childProcessId);
// Update process info in cache
_handlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
return false;
}
else
{
return true;
}
});
_ = NativeMethods.EnumChildWindows(hWindow, callbackptr, 0);
}).Start();
}
return _handlesToProcessCache[hWindow];
}
}
}
}

View File

@@ -0,0 +1,200 @@
// 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;
namespace Microsoft.Plugin.WindowWalker.Components
{
/// <summary>
/// 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
/// </summary>
public class WindowProcess
{
/// <summary>
/// Maximum size of a file name
/// </summary>
private const int MaximumFileNameLength = 1000;
/// <summary>
/// An indicator if the window belongs to an 'Universal Windows Platform (UWP)' process
/// </summary>
private readonly bool _isUwpApp;
/// <summary>
/// Gets the id of the process
/// </summary>
public uint ProcessID
{
get; private set;
}
/// <summary>
/// Gets the id of the thread
/// </summary>
public uint ThreadID
{
get; private set;
}
/// <summary>
/// Gets the name of the process
/// </summary>
public string Name
{
get; private set;
}
/// <summary>
/// Gets a value indicating whether the window belongs to an 'Universal Windows Platform (UWP)' process
/// </summary>
public bool IsUwpApp
{
get { return _isUwpApp; }
}
/// <summary>
/// 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, ...)
/// </summary>
public bool IsShellProcess
{
get
{
IntPtr hShellWindow = NativeMethods.GetShellWindow();
return GetProcessIDFromWindowHandle(hShellWindow) == ProcessID;
}
}
/// <summary>
/// Gets a value indicating whether the process exists on the machine
/// </summary>
public 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;
}
}
}
/// <summary>
/// Gets a value indicating whether full access to the process is denied or not
/// </summary>
public bool IsFullAccessDenied
{
get; private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="WindowProcess"/> class.
/// </summary>
/// <param name="pid">New process id.</param>
/// <param name="tid">New thread id.</param>
/// <param name="name">New process name.</param>
public WindowProcess(uint pid, uint tid, string name)
{
UpdateProcessInfo(pid, tid, name);
_isUwpApp = Name.ToUpperInvariant().Equals("APPLICATIONFRAMEHOST.EXE", StringComparison.Ordinal);
}
/// <summary>
/// Updates the process information of the <see cref="WindowProcess"/> instance.
/// </summary>
/// <param name="pid">New process id.</param>
/// <param name="tid">New thread id.</param>
/// <param name="name">New process name.</param>
public void UpdateProcessInfo(uint pid, uint tid, string name)
{
// TODO: Add verification as to wether 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;
}
/// <summary>
/// Gets the process ID for the window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The process ID</returns>
public static uint GetProcessIDFromWindowHandle(IntPtr hwnd)
{
_ = NativeMethods.GetWindowThreadProcessId(hwnd, out uint processId);
return processId;
}
/// <summary>
/// Gets the thread ID for the window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The thread ID</returns>
public static uint GetThreadIDFromWindowHandle(IntPtr hwnd)
{
uint threadId = NativeMethods.GetWindowThreadProcessId(hwnd, out _);
return threadId;
}
/// <summary>
/// Gets the process name for the process ID
/// </summary>
/// <param name="pid">The id of the process/param>
/// <returns>A string representing the process name or an empty string if the function fails</returns>
public static string GetProcessNameFromProcessID(uint pid)
{
IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, true, (int)pid);
StringBuilder processName = new StringBuilder(MaximumFileNameLength);
if (NativeMethods.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0)
{
_ = NativeMethods.CloseHandleIfNotNull(processHandle);
return processName.ToString().Split('\\').Reverse().ToArray()[0];
}
else
{
_ = NativeMethods.CloseHandleIfNotNull(processHandle);
return string.Empty;
}
}
/// <summary>
/// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not.
/// </summary>
/// <param name="pid">The process ID of the process</param>
/// <returns>True if denied and false if not.</returns>
private static bool TestProcessAccessUsingAllAccessFlag(uint pid)
{
IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.AllAccess, true, (int)pid);
if (NativeMethods.GetLastWin32Error() == 5)
{
// Error 5 = ERROR_ACCESS_DENIED
_ = NativeMethods.CloseHandleIfNotNull(processHandle);
return true;
}
else
{
_ = NativeMethods.CloseHandleIfNotNull(processHandle);
return false;
}
}
}
}

View File

@@ -13,8 +13,6 @@ namespace Microsoft.Plugin.WindowWalker
{
public class Main : IPlugin, IPluginI18n
{
private static List<SearchResult> _results = new List<SearchResult>();
private string IconPath { get; set; }
private PluginInitContext Context { get; set; }
@@ -25,7 +23,6 @@ namespace Microsoft.Plugin.WindowWalker
static Main()
{
SearchController.Instance.OnSearchResultUpdateEventHandler += SearchResultUpdated;
OpenWindows.Instance.UpdateOpenWindowsList();
}
@@ -37,18 +34,22 @@ namespace Microsoft.Plugin.WindowWalker
}
OpenWindows.Instance.UpdateOpenWindowsList();
SearchController.Instance.UpdateSearchText(query.Search).Wait();
SearchController.Instance.UpdateSearchText(query.Search);
List<SearchResult> searchControllerResults = SearchController.Instance.SearchMatches;
return _results.Select(x => new Result()
return searchControllerResults.Select(x => new Result()
{
Title = x.Result.Title,
IcoPath = IconPath,
SubTitle = Properties.Resources.wox_plugin_windowwalker_running + ": " + x.Result.ProcessName,
SubTitle = Properties.Resources.wox_plugin_windowwalker_running + ": " + x.Result.ProcessInfo.Name,
Action = c =>
{
x.Result.SwitchToWindow();
return true;
},
// For debugging you can remove the comment sign in the next line.
// ToolTipData = new ToolTipData(x.Result.Title, $"hWnd: {x.Result.Hwnd}\nWindow class: {x.Result.ClassName}\nProcess ID: {x.Result.ProcessInfo.ProcessID}\nThread ID: {x.Result.ProcessInfo.ThreadID}\nProcess: {x.Result.ProcessInfo.Name}\nProcess exists: {x.Result.ProcessInfo.DoesExist}\nIs full access denied: {x.Result.ProcessInfo.IsFullAccessDenied}\nIs uwp app: {x.Result.ProcessInfo.IsUwpApp}\nIs ShellProcess: {x.Result.ProcessInfo.IsShellProcess}\nIs window cloaked: {x.Result.IsCloaked}\nWindow cloak state: {x.Result.GetWindowCloakState()}"),
}).ToList();
}
@@ -86,10 +87,5 @@ namespace Microsoft.Plugin.WindowWalker
{
return Properties.Resources.wox_plugin_windowwalker_plugin_description;
}
private static void SearchResultUpdated(object sender, SearchController.SearchResultUpdateEventArgs e)
{
_results = SearchController.Instance.SearchMatches;
}
}
}