diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f561fcb1dcf..f0fcd48ffc0 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -128,12 +128,12 @@ public string BadgeIcoPath /// /// Delegate to load an icon for this result. /// - public IconDelegate Icon { get; set; } + public IconDelegate Icon = null; /// /// Delegate to load an icon for the badge of this result. /// - public IconDelegate BadgeIcon { get; set; } + public IconDelegate BadgeIcon = null; /// /// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons) diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml index ea6e54fef7a..ddabd31f8fd 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml @@ -10,6 +10,7 @@ kill {0} processes kill all instances + Show title for processes with visible windows Put processes with visible windows on the top \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs index 4f5d1becdd9..8f5ba4bd237 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs @@ -81,7 +81,10 @@ private List CreateResultsFromQuery(Query query) // Filter processes based on search term var searchTerm = query.Search; var processlist = new List(); - var processWindowTitle = ProcessHelper.GetProcessesWithNonEmptyWindowTitle(); + var processWindowTitle = + Settings.ShowWindowTitle || Settings.PutVisibleWindowProcessesTop ? + ProcessHelper.GetProcessesWithNonEmptyWindowTitle() : + new Dictionary(); if (string.IsNullOrWhiteSpace(searchTerm)) { foreach (var p in allPocessList) @@ -91,12 +94,22 @@ private List CreateResultsFromQuery(Query query) if (processWindowTitle.TryGetValue(p.Id, out var windowTitle)) { // Add score to prioritize processes with visible windows - // And use window title for those processes - processlist.Add(new ProcessResult(p, Settings.PutVisibleWindowProcessesTop ? 200 : 0, windowTitle, null, progressNameIdTitle)); + // Use window title for those processes if enabled + processlist.Add(new ProcessResult( + p, + Settings.PutVisibleWindowProcessesTop ? 200 : 0, + Settings.ShowWindowTitle ? windowTitle : progressNameIdTitle, + null, + progressNameIdTitle)); } else { - processlist.Add(new ProcessResult(p, 0, progressNameIdTitle, null, progressNameIdTitle)); + processlist.Add(new ProcessResult( + p, + 0, + progressNameIdTitle, + null, + progressNameIdTitle)); } } } @@ -115,13 +128,17 @@ private List CreateResultsFromQuery(Query query) if (score > 0) { // Add score to prioritize processes with visible windows - // And use window title for those processes + // Use window title for those processes if (Settings.PutVisibleWindowProcessesTop) { score += 200; } - processlist.Add(new ProcessResult(p, score, windowTitle, - score == windowTitleMatch.Score ? windowTitleMatch : null, progressNameIdTitle)); + processlist.Add(new ProcessResult( + p, + score, + Settings.ShowWindowTitle ? windowTitle : progressNameIdTitle, + score == windowTitleMatch.Score ? windowTitleMatch : null, + progressNameIdTitle)); } } else @@ -130,7 +147,12 @@ private List CreateResultsFromQuery(Query query) var score = processNameIdMatch.Score; if (score > 0) { - processlist.Add(new ProcessResult(p, score, progressNameIdTitle, processNameIdMatch, progressNameIdTitle)); + processlist.Add(new ProcessResult( + p, + score, + progressNameIdTitle, + processNameIdMatch, + progressNameIdTitle)); } } } diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs index d7f44ccce51..3867829058f 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -1,9 +1,11 @@ -using Microsoft.Win32.SafeHandles; -using System; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Threading; @@ -72,8 +74,21 @@ public List GetMatchingProcesses() /// public static unsafe Dictionary GetProcessesWithNonEmptyWindowTitle() { - var processDict = new Dictionary(); + // Collect all window handles + var windowHandles = new List(); PInvoke.EnumWindows((hWnd, _) => + { + if (PInvoke.IsWindowVisible(hWnd)) + { + windowHandles.Add(hWnd); + } + return true; + }, IntPtr.Zero); + + // Concurrently process each window handle + var processDict = new ConcurrentDictionary(); + var processedProcessIds = new ConcurrentDictionary(); + Parallel.ForEach(windowHandles, hWnd => { var windowTitle = GetWindowTitle(hWnd); if (!string.IsNullOrWhiteSpace(windowTitle) && PInvoke.IsWindowVisible(hWnd)) @@ -82,20 +97,26 @@ public static unsafe Dictionary GetProcessesWithNonEmptyWindowTitle var result = PInvoke.GetWindowThreadProcessId(hWnd, &processId); if (result == 0u || processId == 0u) { - return false; + return; } - var process = Process.GetProcessById((int)processId); - if (!processDict.ContainsKey((int)processId)) + // Ensure each process ID is processed only once + if (processedProcessIds.TryAdd((int)processId, 0)) { - processDict.Add((int)processId, windowTitle); + try + { + var process = Process.GetProcessById((int)processId); + processDict.TryAdd((int)processId, windowTitle); + } + catch + { + // Handle exceptions (e.g., process exited) + } } } + }); - return true; - }, IntPtr.Zero); - - return processDict; + return new Dictionary(processDict); } private static unsafe string GetWindowTitle(HWND hwnd) diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Settings.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Settings.cs index 916bc6a3979..57cd2ab8697 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Settings.cs @@ -2,6 +2,8 @@ { public class Settings { + public bool ShowWindowTitle { get; set; } = true; + public bool PutVisibleWindowProcessesTop { get; set; } = false; } } diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ViewModels/SettingsViewModel.cs index bacf1ba08c9..0728d9c0fb5 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ViewModels/SettingsViewModel.cs @@ -9,6 +9,12 @@ public SettingsViewModel(Settings settings) Settings = settings; } + public bool ShowWindowTitle + { + get => Settings.ShowWindowTitle; + set => Settings.ShowWindowTitle = value; + } + public bool PutVisibleWindowProcessesTop { get => Settings.PutVisibleWindowProcessesTop; diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/SettingsControl.xaml index d15d6c3e084..b969be4e877 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/SettingsControl.xaml @@ -12,10 +12,16 @@ + +