Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Flow.Launcher.Plugin/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,12 @@ public string BadgeIcoPath
/// <summary>
/// Delegate to load an icon for this result.
/// </summary>
public IconDelegate Icon { get; set; }
public IconDelegate Icon = null;

/// <summary>
/// Delegate to load an icon for the badge of this result.
/// </summary>
public IconDelegate BadgeIcon { get; set; }
public IconDelegate BadgeIcon = null;

/// <summary>
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<system:String x:Key="flowlauncher_plugin_processkiller_kill_all_count">kill {0} processes</system:String>
<system:String x:Key="flowlauncher_plugin_processkiller_kill_instances">kill all instances</system:String>

<system:String x:Key="flowlauncher_plugin_processkiller_show_window_title">Show title for processes with visible windows</system:String>
<system:String x:Key="flowlauncher_plugin_processkiller_put_visible_window_process_top">Put processes with visible windows on the top</system:String>

</ResourceDictionary>
38 changes: 30 additions & 8 deletions Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ private List<Result> CreateResultsFromQuery(Query query)
// Filter processes based on search term
var searchTerm = query.Search;
var processlist = new List<ProcessResult>();
var processWindowTitle = ProcessHelper.GetProcessesWithNonEmptyWindowTitle();
var processWindowTitle =
Settings.ShowWindowTitle || Settings.PutVisibleWindowProcessesTop ?
ProcessHelper.GetProcessesWithNonEmptyWindowTitle() :
new Dictionary<int, string>();
if (string.IsNullOrWhiteSpace(searchTerm))
{
foreach (var p in allPocessList)
Expand All @@ -91,12 +94,22 @@ private List<Result> 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));
}
}
}
Expand All @@ -115,13 +128,17 @@ private List<Result> 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
Expand All @@ -130,7 +147,12 @@ private List<Result> 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));
}
}
}
Expand Down
43 changes: 32 additions & 11 deletions Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -72,8 +74,21 @@ public List<Process> GetMatchingProcesses()
/// </summary>
public static unsafe Dictionary<int, string> GetProcessesWithNonEmptyWindowTitle()
{
var processDict = new Dictionary<int, string>();
// Collect all window handles
var windowHandles = new List<HWND>();
PInvoke.EnumWindows((hWnd, _) =>
{
if (PInvoke.IsWindowVisible(hWnd))
{
windowHandles.Add(hWnd);
}
return true;
}, IntPtr.Zero);

// Concurrently process each window handle
var processDict = new ConcurrentDictionary<int, string>();
var processedProcessIds = new ConcurrentDictionary<int, byte>();
Parallel.ForEach(windowHandles, hWnd =>
{
var windowTitle = GetWindowTitle(hWnd);
if (!string.IsNullOrWhiteSpace(windowTitle) && PInvoke.IsWindowVisible(hWnd))
Expand All @@ -82,20 +97,26 @@ public static unsafe Dictionary<int, string> 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)
Comment on lines +111 to +113
Copy link
Preview

Copilot AI Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider logging or otherwise capturing the caught exception to facilitate debugging, rather than silently ignoring it.

Copilot uses AI. Check for mistakes.

}
}
}
});

return true;
}, IntPtr.Zero);

return processDict;
return new Dictionary<int, string>(processDict);
}

private static unsafe string GetWindowTitle(HWND hwnd)
Expand Down
2 changes: 2 additions & 0 deletions Plugins/Flow.Launcher.Plugin.ProcessKiller/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
{
public class Settings
{
public bool ShowWindowTitle { get; set; } = true;

public bool PutVisibleWindowProcessesTop { get; set; } = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@
<Grid.ColumnDefinitions />
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<CheckBox
Grid.Row="0"
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_processkiller_show_window_title}"
IsChecked="{Binding ShowWindowTitle}" />
<CheckBox
Grid.Row="1"
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
Content="{DynamicResource flowlauncher_plugin_processkiller_put_visible_window_process_top}"
IsChecked="{Binding PutVisibleWindowProcessesTop}" />
</Grid>
Expand Down
Loading