diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
index 4e216b7b26a..35c0418f3ab 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
@@ -55,6 +55,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
index be2a2dd6673..35191b6e0fe 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using Flow.Launcher.Infrastructure;
@@ -63,7 +63,7 @@ public List LoadContextMenus(Result result)
private List CreateResultsFromQuery(Query query)
{
string termToSearch = query.Search;
- var processlist = processHelper.GetMatchingProcesses(termToSearch);
+ var processlist = processHelper.GetMatchingProcesses(termToSearch, _context);
if (!processlist.Any())
{
@@ -79,7 +79,7 @@ private List CreateResultsFromQuery(Query query)
results.Add(new Result()
{
IcoPath = path,
- Title = p.ProcessName + " - " + p.Id,
+ Title = $"{p.ProcessName} - {p.Id}" + (pr.Port != 0 ? $" - [{pr.Port}]" : ""),
SubTitle = path,
TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
Score = pr.Score,
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/PortHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/PortHelper.cs
new file mode 100644
index 00000000000..08359f84b79
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/PortHelper.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Management;
+
+namespace Flow.Launcher.Plugin.ProcessKiller
+{
+ internal class PortDetail
+ {
+ public int Port { get; set; }
+ public int ProcessID { get; set; }
+ public string ProcessName { get; set; }
+ public Process Process { get; set; }
+ public string Path { get; set; }
+
+ public override string ToString()
+ {
+ return $@" Process Name: {ProcessName}, Process ID: {ProcessID}, Port: {Port}, Path : {Path}";
+ }
+ }
+
+ ///
+ /// Usage:
+ /// int port = 8081
+ /// TcpHelperUtil tcpHelper = new TcpHelperUtil();
+ /// var details = tcpHelper.GetPortDetails(port);
+ /// if (details.Item1)
+ /// {
+ /// Console.WriteLine("Port {0} in Use",port);
+ /// Console.WriteLine(details.Item2.ToString());
+ /// }else
+ /// {
+ /// Console.WriteLine("Port {0} is free ",port);
+ /// }
+ ///
+ internal class PortHelper
+ {
+ private const short MINIMUM_TOKEN_IN_A_LINE = 5;
+ private const string COMMAND_EXE = "cmd";
+
+ private static string ClassName => nameof(PortHelper);
+
+ public static (bool Result, PortDetail Detail) GetPortDetails(int port, PluginInitContext context)
+ {
+ var portDetail = new PortDetail();
+
+ // execute netstat command for the given port
+ string commandArgument = string.Format("/c netstat -an -o -p tcp|findstr \":{0}.*LISTENING\"", port);
+
+ string commandOut = ExecuteCommandAndCaptureOutput(COMMAND_EXE, commandArgument, context);
+ if (string.IsNullOrEmpty(commandOut))
+ {
+ // port is not in use
+ return (false, portDetail);
+ }
+
+ var stringTokens = commandOut.Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
+ if (stringTokens.Length < MINIMUM_TOKEN_IN_A_LINE)
+ {
+ return (false, portDetail);
+ }
+
+ // split host:port
+ var hostPortTokens = stringTokens[1].Split(new char[] { ':' });
+ if (hostPortTokens.Length < 2)
+ {
+ return (false, portDetail);
+ }
+
+ if (!int.TryParse(hostPortTokens[1], out var portFromHostPortToken))
+ {
+ return (false, portDetail);
+ }
+
+ if (portFromHostPortToken != port)
+ {
+ return (false, portDetail);
+ }
+
+ portDetail.Port = port;
+ portDetail.ProcessID = int.Parse(stringTokens[4].Trim());
+ (string Name, string Path) processNameAndPath;
+ try
+ {
+ processNameAndPath = GetProcessNameAndCommandLineArgs(portDetail.ProcessID, context);
+ portDetail.ProcessName = processNameAndPath.Name;
+ portDetail.Path = processNameAndPath.Path;
+ portDetail.Process = Process.GetProcessById(portDetail.ProcessID);
+ return (true, portDetail);
+ }
+ catch (Exception e)
+ {
+ context.API.LogException(ClassName, "Failed to get process name and path", e);
+ }
+
+ return (false, portDetail);
+ }
+
+ ///
+ /// Using WMI API to get process name and path instead of
+ /// Process.GetProcessById, because if calling process ins
+ /// 32 bit and given process id is 64 bit, caller will not be able to
+ /// get the process name
+ ///
+ ///
+ ///
+ ///
+ private static (string Name, string Path) GetProcessNameAndCommandLineArgs(int processID, PluginInitContext context)
+ {
+ var name = string.Empty;
+ var path = string.Empty;
+ string query = string.Format("Select Name,ExecutablePath from Win32_Process WHERE ProcessId='{0}'", processID);
+ try
+ {
+ ObjectQuery wql = new ObjectQuery(query);
+ ManagementObjectSearcher searcher = new ManagementObjectSearcher(wql);
+ ManagementObjectCollection results = searcher.Get();
+
+ // interested in first result.
+ foreach (var item in results.Cast())
+ {
+ name = Convert.ToString(item["Name"]);
+ path = Convert.ToString(item["ExecutablePath"]);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ context.API.LogException(ClassName, "Failed to get process name and path", e);
+ }
+
+ return (name, path);
+ }
+
+ ///
+ /// Execute the given command and captures the output
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static string ExecuteCommandAndCaptureOutput(string commandName, string arguments, PluginInitContext context)
+ {
+ string commandOut = string.Empty;
+ Process process = new Process();
+ process.StartInfo.FileName = commandName;
+ process.StartInfo.Arguments = arguments;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.Start();
+
+ commandOut = process.StandardOutput.ReadToEnd();
+ string errors = process.StandardError.ReadToEnd();
+ try
+ {
+ process.WaitForExit(TimeSpan.FromSeconds(2).Milliseconds);
+ }
+ catch (Exception exp)
+ {
+ context.API.LogException(ClassName, $"Failed to ExecuteCommandAndCaptureOutput {commandName + arguments}", exp);
+ }
+
+ return commandOut;
+ }
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
index 519e8a79297..7ade32b23a6 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -36,14 +36,25 @@ internal class ProcessHelper
///
/// Returns a ProcessResult for evey running non-system process whose name matches the given searchTerm
///
- public List GetMatchingProcesses(string searchTerm)
+ public List GetMatchingProcesses(string searchTerm, PluginInitContext context)
{
var processlist = new List();
+ bool canConvert = int.TryParse(searchTerm, out var portNum);
+ bool portResult = false;
+ PortDetail portDetail = new();
+ if (canConvert)
+ {
+ (portResult, portDetail) = PortHelper.GetPortDetails(portNum, context);
+ }
+
foreach (var p in Process.GetProcesses())
{
if (IsSystemProcess(p)) continue;
-
+ if (portResult && portDetail.Process.Id == p.Id)
+ {
+ continue;
+ }
if (string.IsNullOrWhiteSpace(searchTerm))
{
// show all non-system processes
@@ -59,6 +70,11 @@ public List GetMatchingProcesses(string searchTerm)
}
}
+ if (portResult)
+ {
+ var p = portDetail.Process;
+ processlist.Add(new ProcessResult(p, StringMatcher.FuzzySearch(searchTerm, p.ProcessName + p.Id).Score, portNum));
+ }
return processlist;
}
@@ -83,6 +99,26 @@ public void TryKill(Process p)
catch (Exception e)
{
Log.Exception($"{nameof(ProcessHelper)}", $"Failed to kill process {p.ProcessName}", e);
+ TryKillRunAs(p);
+ }
+ }
+
+ public void TryKillRunAs(Process p)
+ {
+ try
+ {
+ Process process = new Process();
+ ProcessStartInfo startInfo = new ProcessStartInfo();
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ startInfo.FileName = "powershell.exe";
+ startInfo.Arguments = $"Start cmd.exe -ArgumentList \"/k\",\"taskkill\",\"/f\",\"/pid\", \"{p.Id}\" -Verb Runas";
+ startInfo.UseShellExecute = false;
+ process.StartInfo = startInfo;
+ process.Start();
+ }
+ catch(Exception e)
+ {
+ Log.Exception($"{nameof(ProcessHelper)}", $"Failed to kill process again of run as admin {p.ProcessName}", e);
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs
index 03856677e63..d3de0456b56 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs
@@ -10,8 +10,17 @@ public ProcessResult(Process process, int score)
Score = score;
}
+ public ProcessResult(Process process, int score, int port)
+ {
+ Process = process;
+ Score = score;
+ Port = port;
+ }
+
public Process Process { get; }
public int Score { get; }
+
+ public int Port { set; get; } = 0;
}
}
\ No newline at end of file