diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index b97bffd45..0c69a6b9b 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 1.0.0 - 1.0.0 - 1.0.0 - 1.0.0 + 1.1.0 + 1.1.0 + 1.1.0 + 1.1.0 Flow.Launcher.Plugin Flow-Launcher MIT diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 2be28daf1..70155a8a0 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -26,6 +26,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} {049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE} {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E} + {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}" @@ -70,6 +71,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 +300,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 +330,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.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 10b3edd96..91b27edb1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -4,7 +4,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.0.0", + "Version": "1.1.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", 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). + +![image](https://user-images.githubusercontent.com/697917/86478286-b4697700-bd52-11ea-875a-b3b90fa34bf2.png) 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 diff --git a/SolutionAssemblyInfo.cs b/SolutionAssemblyInfo.cs index 1482bce9e..9dce85a1e 100644 --- a/SolutionAssemblyInfo.cs +++ b/SolutionAssemblyInfo.cs @@ -16,6 +16,6 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.0.0")] -[assembly: AssemblyFileVersion("1.0.0")] -[assembly: AssemblyInformationalVersion("1.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.1.1")] +[assembly: AssemblyFileVersion("1.1.1")] +[assembly: AssemblyInformationalVersion("1.1.1")] \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index a4000d1a4..81057e43a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.0.0.{build}' +version: '1.1.1.{build}' init: - ps: |