CmdPal: Remove dead code from Window Walker and cleanup (#45570)

## Summary of the Pull Request

This PR is a light cleanup on Window Walker built-in extension:

- Removes unused types
- Removes redundant code
- Fixes inconsistent naming conventions
- De-LINQ-ify
- Fixes XML doc
- Updates language constructs to the latest (use of
System.Threading.Lock, Collection expressions)
- Updates types and members visibility and makes static classes static

⚠️ Trickiest part are 44ac1c1 and 26c946c that removes redundant null
checks.
This commit is contained in:
Jiří Polášek
2026-02-24 13:29:58 +01:00
committed by GitHub
parent 4f5837d4e9
commit 07b8915e19
25 changed files with 169 additions and 513 deletions

View File

@@ -3,15 +3,10 @@
// See the LICENSE file in the project root for more information.
// Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.WindowWalker.Components;
@@ -20,26 +15,11 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Components;
/// </summary>
internal sealed class Window
{
/// <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();
/// <summary>
/// An instance of <see cref="WindowProcess"/> that contains the process information for the window
/// </summary>
private readonly WindowProcess processInfo;
/// <summary>
/// An instance of <see cref="VDesktop"/> that contains the desktop information for the window
/// </summary>
private readonly VDesktop desktopInfo;
private static readonly Dictionary<IntPtr, WindowProcess> HandlesToProcessCache = new();
/// <summary>
/// Gets the title of the window (the string displayed at the top of the window)
@@ -48,11 +28,11 @@ internal sealed class Window
{
get
{
var sizeOfTitle = NativeMethods.GetWindowTextLength(hwnd);
var sizeOfTitle = NativeMethods.GetWindowTextLength(Hwnd);
if (sizeOfTitle++ > 0)
{
StringBuilder titleBuffer = new StringBuilder(sizeOfTitle);
var numCharactersWritten = NativeMethods.GetWindowText(hwnd, titleBuffer, sizeOfTitle);
var titleBuffer = new StringBuilder(sizeOfTitle);
var numCharactersWritten = NativeMethods.GetWindowText(Hwnd, titleBuffer, sizeOfTitle);
if (numCharactersWritten == 0)
{
return string.Empty;
@@ -70,17 +50,17 @@ internal sealed class Window
/// <summary>
/// Gets the handle to the window
/// </summary>
internal IntPtr Hwnd => hwnd;
internal IntPtr Hwnd { get; }
/// <summary>
/// Gets the object of with the process information of the window
/// </summary>
internal WindowProcess Process => processInfo;
internal WindowProcess Process { get; }
/// <summary>
/// Gets the object of with the desktop information of the window
/// </summary>
internal VDesktop Desktop => desktopInfo;
internal VDesktop Desktop { get; }
/// <summary>
/// Gets the name of the class for the window represented
@@ -130,7 +110,7 @@ internal sealed class Window
/// <summary>
/// Gets a value indicating whether the window is minimized
/// </summary>
internal bool Minimized => GetWindowSizeState() == WindowSizeState.Minimized;
private bool Minimized => GetWindowSizeState() == WindowSizeState.Minimized;
/// <summary>
/// Initializes a new instance of the <see cref="Window"/> class.
@@ -140,9 +120,9 @@ internal sealed class Window
internal Window(IntPtr hwnd)
{
// TODO: Add verification as to whether the window handle is valid
this.hwnd = hwnd;
processInfo = CreateWindowProcessInstance(hwnd);
desktopInfo = WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetWindowDesktop(hwnd);
Hwnd = hwnd;
Process = CreateWindowProcessInstance(hwnd);
Desktop = WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetWindowDesktop(hwnd);
}
/// <summary>
@@ -155,7 +135,7 @@ internal sealed class Window
// to use ShowWindow for switching tabs in IE
// 2) SetForegroundWindow fails on minimized windows
// Using Ordinal since this is internal
if (processInfo.Name?.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) == true || !Minimized)
if (Process.Name?.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) == true || !Minimized)
{
NativeMethods.SetForegroundWindow(Hwnd);
}
@@ -174,7 +154,7 @@ internal sealed class Window
/// <summary>
/// Helper function to close the window
/// </summary>
internal void CloseThisWindowHelper()
private void CloseThisWindowHelper()
{
_ = NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_SYSCOMMAND, Win32Constants.SC_CLOSE, 0, 0x0000, 5000, out _);
}
@@ -184,7 +164,7 @@ internal sealed class Window
/// </summary>
internal void CloseThisWindow()
{
Thread thread = new(new ThreadStart(CloseThisWindowHelper));
Thread thread = new(CloseThisWindowHelper);
thread.Start();
}
@@ -197,47 +177,47 @@ internal sealed class Window
{
icon = null;
if (hwnd == IntPtr.Zero)
if (Hwnd == IntPtr.Zero)
{
return false;
}
// Try WM_GETICON with SendMessageTimeout
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_BIG, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out var result) != 0 && result != 0)
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, Win32Constants.ICON_BIG, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out var result) != 0 && result != 0)
{
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
NativeMethods.DestroyIcon((IntPtr)result);
icon = System.Drawing.Icon.FromHandle(result);
NativeMethods.DestroyIcon(result);
return true;
}
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, Win32Constants.ICON_SMALL, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
{
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
NativeMethods.DestroyIcon((IntPtr)result);
icon = System.Drawing.Icon.FromHandle(result);
NativeMethods.DestroyIcon(result);
return true;
}
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL2, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, Win32Constants.ICON_SMALL2, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
{
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
NativeMethods.DestroyIcon((IntPtr)result);
icon = System.Drawing.Icon.FromHandle(result);
NativeMethods.DestroyIcon(result);
return true;
}
// Fallback to GetClassLongPtr
var iconHandle = NativeMethods.GetClassLongPtr(hwnd, Win32Constants.GCLP_HICON);
var iconHandle = NativeMethods.GetClassLongPtr(Hwnd, Win32Constants.GCLP_HICON);
if (iconHandle != IntPtr.Zero)
{
icon = System.Drawing.Icon.FromHandle(iconHandle);
NativeMethods.DestroyIcon((IntPtr)iconHandle);
NativeMethods.DestroyIcon(iconHandle);
return true;
}
iconHandle = NativeMethods.GetClassLongPtr(hwnd, Win32Constants.GCLP_HICONSM);
iconHandle = NativeMethods.GetClassLongPtr(Hwnd, Win32Constants.GCLP_HICONSM);
if (iconHandle != IntPtr.Zero)
{
icon = System.Drawing.Icon.FromHandle(iconHandle);
NativeMethods.DestroyIcon((IntPtr)iconHandle);
NativeMethods.DestroyIcon(iconHandle);
return true;
}
@@ -251,16 +231,16 @@ internal sealed class Window
public override string ToString()
{
// Using CurrentCulture since this is user facing
return Title + " (" + processInfo.Name?.ToUpper(CultureInfo.CurrentCulture) + ")";
return Title + " (" + Process.Name?.ToUpper(CultureInfo.CurrentCulture) + ")";
}
/// <summary>
/// Returns what the window size is
/// </summary>
/// <returns>The state (minimized, maximized, etc..) of the window</returns>
internal WindowSizeState GetWindowSizeState()
/// <returns>The state (minimized, maximized, etc...) of the window</returns>
private WindowSizeState GetWindowSizeState()
{
NativeMethods.GetWindowPlacement(Hwnd, out WINDOWPLACEMENT placement);
NativeMethods.GetWindowPlacement(Hwnd, out var placement);
switch (placement.ShowCmd)
{
@@ -304,7 +284,7 @@ internal sealed class Window
case (int)DwmWindowCloakStates.CloakedApp:
return WindowCloakState.App;
case (int)DwmWindowCloakStates.CloakedShell:
return WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.IsWindowCloakedByVirtualDesktopManager(hwnd, Desktop.Id) ? WindowCloakState.OtherDesktop : WindowCloakState.Shell;
return WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.IsWindowCloakedByVirtualDesktopManager(Hwnd, Desktop.Id) ? WindowCloakState.OtherDesktop : WindowCloakState.Shell;
case (int)DwmWindowCloakStates.CloakedInherited:
return WindowCloakState.Inherited;
default:
@@ -332,7 +312,7 @@ internal sealed class Window
/// <returns>Class name</returns>
private static string GetWindowClassName(IntPtr hwnd)
{
StringBuilder windowClassName = new StringBuilder(300);
var windowClassName = new StringBuilder(300);
var numCharactersWritten = NativeMethods.GetClassName(hwnd, windowClassName, windowClassName.MaxCapacity);
if (numCharactersWritten == 0)
@@ -350,16 +330,16 @@ internal sealed class Window
/// <returns>A new Instance of type <see cref="WindowProcess"/></returns>
private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
{
lock (_handlesToProcessCache)
lock (HandlesToProcessCache)
{
if (_handlesToProcessCache.Count > 7000)
if (HandlesToProcessCache.Count > 7000)
{
Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
_handlesToProcessCache.Clear();
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))
if (!HandlesToProcessCache.ContainsKey(hWindow))
{
// Get process ID and name
var processId = WindowProcess.GetProcessIDFromWindowHandle(hWindow);
@@ -368,23 +348,23 @@ internal sealed class Window
if (processName.Length != 0)
{
_handlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
HandlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
}
else
{
// For the dwm process we cannot receive the name. This is no problem because the window isn't part of result list.
ExtensionHost.LogMessage(new LogMessage() { Message = $"Invalid process {processId} ({processName}) for window handle {hWindow}." });
_handlesToProcessCache.Add(hWindow, new WindowProcess(0, 0, string.Empty));
ExtensionHost.LogMessage(new LogMessage { Message = $"Invalid process {processId} ({processName}) for window handle {hWindow}." });
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].IsUwpAppFrameHost)
if (HandlesToProcessCache[hWindow].IsUwpAppFrameHost)
{
new Task(() =>
{
EnumWindowsProc callbackptr = new EnumWindowsProc((IntPtr hwnd, IntPtr lParam) =>
var callbackptr = new EnumWindowsProc((hwnd, _) =>
{
// 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".)
@@ -395,9 +375,9 @@ internal sealed class Window
var childProcessName = WindowProcess.GetProcessNameFromProcessID(childProcessId);
// Update process info in cache
lock (_handlesToProcessCache)
lock (HandlesToProcessCache)
{
_handlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
HandlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
}
return false;
@@ -411,7 +391,7 @@ internal sealed class Window
}).Start();
}
return _handlesToProcessCache[hWindow];
return HandlesToProcessCache[hWindow];
}
}
}