diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln
index 2be28daf1..43c012b5b 100644
--- a/Flow.Launcher.sln
+++ b/Flow.Launcher.sln
@@ -70,6 +70,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Calcul
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explorer", "Plugins\Flow.Launcher.Plugin.Explorer\Flow.Launcher.Plugin.Explorer.csproj", "{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -297,6 +299,18 @@ Global
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x64.Build.0 = Release|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.ActiveCfg = Release|Any CPU
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F}.Release|x86.Build.0 = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x64.Build.0 = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Debug|x86.Build.0 = Debug|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.ActiveCfg = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
+ {588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -315,6 +329,7 @@ Global
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
+ {588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
new file mode 100644
index 000000000..ab84aa54a
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
@@ -0,0 +1,55 @@
+
+
+
+ netcoreapp3.1
+ Flow.Launcher.Plugin.ProcessKiller
+ Flow.Launcher.Plugin.ProcessKiller
+ Flow-Launcher
+ https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller
+ https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller
+ flow-launcher flow-plugin
+ false
+ false
+
+
+
+ true
+ full
+ false
+ ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.ProcessKiller\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+
+ pdbonly
+ true
+ ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.ProcessKiller\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+ PreserveNewest
+
+
+ Designer
+ MSBuild:Compile
+ PreserveNewest
+
+
+ Always
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Images/app.png b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Images/app.png
new file mode 100644
index 000000000..fa76a5d29
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Images/app.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml
new file mode 100644
index 000000000..2eee31745
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/en.xaml
@@ -0,0 +1,11 @@
+
+
+ Process Killer
+ Kill running processes from Flow Launcher
+
+ kill all "{0}" processes
+ kill all instances
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
new file mode 100644
index 000000000..22c84b20f
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using System.Dynamic;
+using System.Runtime.InteropServices;
+using Flow.Launcher.Infrastructure;
+using Flow.Launcher.Infrastructure.Logger;
+
+namespace Flow.Launcher.Plugin.ProcessKiller
+{
+ public class Main : IPlugin, IPluginI18n, IContextMenu
+ {
+ private ProcessHelper processHelper = new ProcessHelper();
+
+ private static PluginInitContext _context;
+
+ public void Init(PluginInitContext context)
+ {
+ _context = context;
+ }
+
+ public List Query(Query query)
+ {
+ var termToSearch = query.Terms.Length <= 1
+ ? null
+ : string.Join(Plugin.Query.TermSeperater, query.Terms.Skip(1)).ToLower();
+
+ var processlist = processHelper.GetMatchingProcesses(termToSearch);
+
+ return !processlist.Any()
+ ? null
+ : CreateResultsFromProcesses(processlist, termToSearch);
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return _context.API.GetTranslation("flowlauncher_plugin_processkiller_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return _context.API.GetTranslation("flowlauncher_plugin_processkiller_plugin_description");
+ }
+
+ public List LoadContextMenus(Result result)
+ {
+ var menuOptions = new List();
+ var processPath = result.SubTitle;
+
+ // get all non-system processes whose file path matches that of the given result (processPath)
+ var similarProcesses = processHelper.GetSimilarProcesses(processPath);
+
+ menuOptions.Add(new Result
+ {
+ Title = _context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_instances"),
+ SubTitle = processPath,
+ Action = _ =>
+ {
+ foreach (var p in similarProcesses)
+ {
+ processHelper.TryKill(p);
+ }
+
+ return true;
+ },
+ IcoPath = processPath
+ });
+
+ return menuOptions;
+ }
+
+ private List CreateResultsFromProcesses(List processlist, string termToSearch)
+ {
+ var results = new List();
+
+ foreach (var pr in processlist)
+ {
+ var p = pr.Process;
+ var path = processHelper.TryGetProcessFilename(p);
+ results.Add(new Result()
+ {
+ IcoPath = path,
+ Title = p.ProcessName + " - " + p.Id,
+ SubTitle = path,
+ TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
+ Score = pr.Score,
+ Action = (c) =>
+ {
+ processHelper.TryKill(p);
+ return true;
+ }
+ });
+ }
+
+ // When there are multiple results AND all of them are instances of the same executable
+ // add a quick option to kill them all at the top of the results.
+ var firstResult = results.FirstOrDefault()?.SubTitle;
+ if (processlist.Count > 1 && !string.IsNullOrEmpty(termToSearch) && results.All(r => r.SubTitle == firstResult))
+ {
+ results.Insert(0, new Result()
+ {
+ IcoPath = "Images/app.png",
+ Title = string.Format(_context.API.GetTranslation("flowlauncher_plugin_processkiller_kill_all"), termToSearch),
+ SubTitle = "",
+ Score = 200,
+ Action = (c) =>
+ {
+ foreach (var p in processlist)
+ {
+ processHelper.TryKill(p.Process);
+ }
+
+ return true;
+ }
+ });
+ }
+
+ return results;
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
new file mode 100644
index 000000000..16a8687e6
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -0,0 +1,125 @@
+using Flow.Launcher.Infrastructure;
+using Flow.Launcher.Infrastructure.Logger;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Flow.Launcher.Plugin.ProcessKiller
+{
+ internal class ProcessHelper
+ {
+ private readonly HashSet _systemProcessList = new HashSet()
+ {
+ "conhost",
+ "svchost",
+ "idle",
+ "system",
+ "rundll32",
+ "csrss",
+ "lsass",
+ "lsm",
+ "smss",
+ "wininit",
+ "winlogon",
+ "services",
+ "spoolsv",
+ "explorer"
+ };
+
+ private bool IsSystemProcess(Process p) => _systemProcessList.Contains(p.ProcessName.ToLower());
+
+ ///
+ /// Returns a ProcessResult for evey running non-system process whose name matches the given searchTerm
+ ///
+ public List GetMatchingProcesses(string searchTerm)
+ {
+ var processlist = new List();
+
+ foreach (var p in Process.GetProcesses())
+ {
+ if (IsSystemProcess(p)) continue;
+
+ if (string.IsNullOrWhiteSpace(searchTerm))
+ {
+ // show all non-system processes
+ processlist.Add(new ProcessResult(p, 0));
+ }
+ else
+ {
+ var score = StringMatcher.FuzzySearch(searchTerm, p.ProcessName + p.Id).Score;
+ if (score > 0)
+ {
+ processlist.Add(new ProcessResult(p, score));
+ }
+ }
+ }
+
+ return processlist;
+ }
+
+ ///
+ /// Returns all non-system processes whose file path matches the given processPath
+ ///
+ public IEnumerable GetSimilarProcesses(string processPath)
+ {
+ return Process.GetProcesses().Where(p => !IsSystemProcess(p) && TryGetProcessFilename(p) == processPath);
+ }
+
+ public void TryKill(Process p)
+ {
+ try
+ {
+ if (!p.HasExited)
+ {
+ p.Kill();
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Exception($"|ProcessKiller.CreateResultsFromProcesses|Failed to kill process {p.ProcessName}", e);
+ }
+ }
+
+ public string TryGetProcessFilename(Process p)
+ {
+ try
+ {
+ int capacity = 2000;
+ StringBuilder builder = new StringBuilder(capacity);
+ IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
+ if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity))
+ {
+ return String.Empty;
+ }
+
+ return builder.ToString();
+ }
+ catch
+ {
+ return "";
+ }
+ }
+
+ [Flags]
+ private enum ProcessAccessFlags : uint
+ {
+ QueryLimitedInformation = 0x00001000
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern bool QueryFullProcessImageName(
+ [In] IntPtr hProcess,
+ [In] int dwFlags,
+ [Out] StringBuilder lpExeName,
+ ref int lpdwSize);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern IntPtr OpenProcess(
+ ProcessAccessFlags processAccess,
+ bool bInheritHandle,
+ int processId);
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs
new file mode 100644
index 000000000..03856677e
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics;
+
+namespace Flow.Launcher.Plugin.ProcessKiller
+{
+ internal class ProcessResult
+ {
+ public ProcessResult(Process process, int score)
+ {
+ Process = process;
+ Score = score;
+ }
+
+ public Process Process { get; }
+
+ public int Score { get; }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Readme.md b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Readme.md
new file mode 100644
index 000000000..3307c917e
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Readme.md
@@ -0,0 +1,8 @@
+Process Killer Plugin
+=====================
+
+A Flow-Launcher plugin that allows you to kill running processes.
+
+Based on Wox.Plugin.ProcessKiller, originally created by [cxfksword](https://github.com/cxfksword/Wox.Plugin.ProcessKiller) and improved by [theClueless](https://github.com/theClueless/Wox.Plugins/tree/master/Wox.Plugin.ProcessKiller).
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json
new file mode 100644
index 000000000..f2e1eca12
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID":"b64d0a79-329a-48b0-b53f-d658318a1bf6",
+ "ActionKeyword":"kill",
+ "Name":"Process Killer",
+ "Description":"kill running processes from Flow",
+ "Author":"Flow-Launcher",
+ "Version":"1.0.0",
+ "Language":"csharp",
+ "Website":"https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller",
+ "IcoPath":"Images\\app.png",
+ "ExecuteFileName":"Flow.Launcher.Plugin.ProcessKiller.dll"
+}
\ No newline at end of file