diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs index 03b563c9b..098e668cb 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs @@ -6,7 +6,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller { public class Main : IPlugin, IPluginI18n, IContextMenu { - private ProcessHelper processHelper = new ProcessHelper(); + private readonly ProcessHelper processHelper = new(); private static PluginInitContext _context; @@ -66,7 +66,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller { string termToSearch = query.Search; var processList = processHelper.GetMatchingProcesses(termToSearch); - var processWithNonEmptyMainWindowTitleList = processHelper.GetProcessesWithNonEmptyWindowTitle(); + var processWithNonEmptyMainWindowTitleList = ProcessHelper.GetProcessesWithNonEmptyWindowTitle(); if (!processList.Any()) { diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt index 7fa794755..13bf27932 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt @@ -1,2 +1,7 @@ QueryFullProcessImageName -OpenProcess \ No newline at end of file +OpenProcess +EnumWindows +GetWindowTextLength +GetWindowText +IsWindowVisible +GetWindowThreadProcessId \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs index d8873bc20..5f58dc6b1 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -13,21 +13,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller { internal class ProcessHelper { - [DllImport("user32.dll")] - private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); - - private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); - - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - - [DllImport("user32.dll")] - private static extern bool IsWindowVisible(IntPtr hWnd); - - [DllImport("user32.dll")] - private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - - private readonly HashSet _systemProcessList = new HashSet() + private readonly HashSet _systemProcessList = new() { "conhost", "svchost", @@ -79,22 +65,25 @@ namespace Flow.Launcher.Plugin.ProcessKiller /// /// Returns a dictionary of process IDs and their window titles for processes that have a visible main window with a non-empty title. /// - public Dictionary GetProcessesWithNonEmptyWindowTitle() + public static unsafe Dictionary GetProcessesWithNonEmptyWindowTitle() { var processDict = new Dictionary(); - EnumWindows((hWnd, lParam) => + PInvoke.EnumWindows((hWnd, lParam) => { - StringBuilder windowTitle = new StringBuilder(); - GetWindowText(hWnd, windowTitle, windowTitle.Capacity); - - if (!string.IsNullOrWhiteSpace(windowTitle.ToString()) && IsWindowVisible(hWnd)) + var windowTitle = GetWindowTitle(hWnd); + if (!string.IsNullOrWhiteSpace(windowTitle) && PInvoke.IsWindowVisible(hWnd)) { - GetWindowThreadProcessId(hWnd, out var processId); - var process = Process.GetProcessById((int)processId); + uint processId = 0; + var result = PInvoke.GetWindowThreadProcessId(hWnd, &processId); + if (result == 0u || processId == 0u) + { + return false; + } + var process = Process.GetProcessById((int)processId); if (!processDict.ContainsKey((int)processId)) { - processDict.Add((int)processId, windowTitle.ToString()); + processDict.Add((int)processId, windowTitle); } } @@ -104,6 +93,21 @@ namespace Flow.Launcher.Plugin.ProcessKiller return processDict; } + private static unsafe string GetWindowTitle(HWND hwnd) + { + var capacity = PInvoke.GetWindowTextLength(hwnd) + 1; + int length; + Span buffer = capacity < 1024 ? stackalloc char[capacity] : new char[capacity]; + fixed (char* pBuffer = buffer) + { + // If the window has no title bar or text, if the title bar is empty, + // or if the window or control handle is invalid, the return value is zero. + length = PInvoke.GetWindowText(hwnd, pBuffer, capacity); + } + + return buffer[..length].ToString(); + } + /// /// Returns all non-system processes whose file path matches the given processPath ///