Skip to content

Commit f984223

Browse files
committed
kill 插件允许通过tcp端口搜索,kill失败后尝试管理员权限再次执行
1 parent 0db6679 commit f984223

File tree

5 files changed

+229
-4
lines changed

5 files changed

+229
-4
lines changed

Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
</Content>
5050
</ItemGroup>
5151

52+
<ItemGroup>
53+
<PackageReference Include="System.Management" Version="6.0.0" />
54+
</ItemGroup>
55+
5256
<ItemGroup>
5357
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
5458
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />

Plugins/Flow.Launcher.Plugin.ProcessKiller/Main.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@ private List<Result> CreateResultsFromProcesses(List<ProcessResult> processlist,
8383
results.Add(new Result()
8484
{
8585
IcoPath = path,
86-
Title = p.ProcessName + " - " + p.Id,
86+
Title = p.ProcessName + " - " + p.Id + (pr.Port!=0? $" - [{pr.Port}]":""),
8787
SubTitle = path,
8888
TitleHighlightData = StringMatcher.FuzzySearch(termToSearch, p.ProcessName).MatchData,
8989
Score = pr.Score,
9090
ContextData = p.ProcessName,
9191
AutoCompleteText = $"{_context.CurrentPluginMetadata.ActionKeyword}{Plugin.Query.TermSeparator}{p.ProcessName}",
9292
Action = (c) =>
93-
{
93+
{
9494
processHelper.TryKill(p);
9595
return true;
9696
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
using Flow.Launcher.Infrastructure;
2+
using Flow.Launcher.Infrastructure.Logger;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using System.Management;
8+
9+
namespace Flow.Launcher.Plugin.ProcessKiller
10+
{
11+
internal class PortDetail
12+
{
13+
public int Port { get; set; }
14+
public int ProcessID { get; set; }
15+
public string ProcessName { get; set; }
16+
17+
public Process Process { get; set; }
18+
public string Path { get; set; }
19+
public override string ToString()
20+
{
21+
return string.Format(@" Process Name: {0} ,Process ID: {1} ,
22+
Port: {2} ,\nPath : {3}", ProcessName,
23+
ProcessID, Port, Path);
24+
}
25+
26+
}
27+
/// <summary>
28+
/// Usage:
29+
/// int port = 8081
30+
/// TcpHelperUtil tcpHelper = new TcpHelperUtil();
31+
/// var details = tcpHelper.GetPortDetails(port);
32+
/// if (details.Item1)
33+
/// {
34+
/// Console.WriteLine("Port {0} in Use",port);
35+
/// Console.WriteLine(details.Item2.ToString());
36+
/// }else
37+
/// {
38+
/// Console.WriteLine("Port {0} is free ",port);
39+
/// }
40+
///
41+
/// </summary>
42+
internal class PortHelper
43+
{
44+
private const short MINIMUM_TOKEN_IN_A_LINE = 5;
45+
private const string COMMAND_EXE = "cmd";
46+
47+
public PortHelper()
48+
{
49+
50+
}
51+
52+
public static Tuple<bool, PortDetail> GetPortDetails(int port)
53+
{
54+
PortDetail PortDetail = new PortDetail();
55+
Tuple<bool, PortDetail> result = Tuple.Create(false, PortDetail);
56+
57+
// execute netstat command for the given port
58+
string commandArgument = string.Format("/c netstat -an -o -p tcp|findstr \":{0}.*LISTENING\"", port);
59+
60+
string commandOut = ExecuteCommandAndCaptureOutput(COMMAND_EXE, commandArgument);
61+
if (string.IsNullOrEmpty(commandOut))
62+
{
63+
// port is not in use
64+
return result;
65+
}
66+
67+
var stringTokens = commandOut.Split(default(Char[]), StringSplitOptions.RemoveEmptyEntries);
68+
if (stringTokens.Length < MINIMUM_TOKEN_IN_A_LINE)
69+
{
70+
return result;
71+
}
72+
73+
// split host:port
74+
var hostPortTokens = stringTokens[1].Split(new char[] { ':' });
75+
if (hostPortTokens.Length < 2)
76+
{
77+
return result;
78+
}
79+
80+
int portFromHostPortToken = 0;
81+
if (!int.TryParse(hostPortTokens[1], out portFromHostPortToken))
82+
{
83+
return result;
84+
}
85+
if (portFromHostPortToken != port)
86+
{
87+
return result;
88+
}
89+
90+
PortDetail.Port = port;
91+
PortDetail.ProcessID = int.Parse(stringTokens[4].Trim());
92+
Tuple<string, string> processNameAndPath = null;
93+
try
94+
{
95+
processNameAndPath = GetProcessNameAndCommandLineArgs(PortDetail.ProcessID);
96+
PortDetail.ProcessName = processNameAndPath.Item1;
97+
PortDetail.Path = processNameAndPath.Item2;
98+
PortDetail.Process = Process.GetProcessById(PortDetail.ProcessID);
99+
result = Tuple.Create(true, PortDetail);
100+
}
101+
catch (Exception exp)
102+
{
103+
Console.WriteLine(exp.ToString());
104+
105+
}
106+
107+
return result;
108+
109+
}
110+
/// <summary>
111+
/// Using WMI API to get process name and path instead of
112+
/// Process.GetProcessById, because if calling process ins
113+
/// 32 bit and given process id is 64 bit, caller will not be able to
114+
/// get the process name
115+
/// </summary>
116+
/// <param name="processID"></param>
117+
/// <returns></returns>
118+
private static Tuple<string, string> GetProcessNameAndCommandLineArgs(int processID)
119+
{
120+
Tuple<string, string> result = Tuple.Create(string.Empty, string.Empty);
121+
string query = string.Format("Select Name,ExecutablePath from Win32_Process WHERE ProcessId='{0}'", processID);
122+
try
123+
{
124+
ObjectQuery wql = new ObjectQuery(query);
125+
ManagementObjectSearcher searcher = new ManagementObjectSearcher(wql);
126+
ManagementObjectCollection results = searcher.Get();
127+
128+
// interested in first result.
129+
foreach (ManagementObject item in results)
130+
{
131+
result = Tuple.Create<string, string>(Convert.ToString(item["Name"]),
132+
Convert.ToString(item["ExecutablePath"]));
133+
break;
134+
135+
}
136+
}
137+
catch (Exception)
138+
{
139+
140+
throw;
141+
}
142+
143+
return result;
144+
145+
}
146+
147+
/// <summary>
148+
/// Execute the given command and captures the output
149+
/// </summary>
150+
/// <param name="commandName"></param>
151+
/// <param name="arguments"></param>
152+
/// <returns></returns>
153+
private static string ExecuteCommandAndCaptureOutput(string commandName, string arguments)
154+
{
155+
string commandOut = string.Empty;
156+
Process process = new Process();
157+
process.StartInfo.FileName = commandName;
158+
process.StartInfo.Arguments = arguments;
159+
process.StartInfo.UseShellExecute = false;
160+
process.StartInfo.CreateNoWindow = true;
161+
process.StartInfo.RedirectStandardOutput = true;
162+
process.StartInfo.RedirectStandardError = true;
163+
process.Start();
164+
165+
commandOut = process.StandardOutput.ReadToEnd();
166+
string errors = process.StandardError.ReadToEnd();
167+
try
168+
{
169+
process.WaitForExit(TimeSpan.FromSeconds(2).Milliseconds);
170+
}
171+
catch (Exception exp)
172+
{
173+
Log.Exception($"|ProcessKiller.CreateResultsFromProcesses|Failed to ExecuteCommandAndCaptureOutput {commandName + arguments}", exp);
174+
Console.WriteLine(exp.ToString());
175+
}
176+
return commandOut;
177+
}
178+
}
179+
}

Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,19 @@ public List<ProcessResult> GetMatchingProcesses(string searchTerm)
3838
{
3939
var processlist = new List<ProcessResult>();
4040

41+
int portNum;
42+
bool canConvert = int.TryParse(searchTerm, out portNum);
43+
Tuple<bool, PortDetail> tcpPortListeningProcess = null;
44+
if(canConvert)
45+
tcpPortListeningProcess = PortHelper.GetPortDetails(portNum);
46+
4147
foreach (var p in Process.GetProcesses())
4248
{
4349
if (IsSystemProcess(p)) continue;
44-
50+
if (tcpPortListeningProcess != null && tcpPortListeningProcess.Item1 && tcpPortListeningProcess.Item2.Process.Id == p.Id)
51+
{
52+
continue;
53+
}
4554
if (string.IsNullOrWhiteSpace(searchTerm))
4655
{
4756
// show all non-system processes
@@ -56,7 +65,11 @@ public List<ProcessResult> GetMatchingProcesses(string searchTerm)
5665
}
5766
}
5867
}
59-
68+
if (tcpPortListeningProcess != null && tcpPortListeningProcess.Item1)
69+
{
70+
var p = tcpPortListeningProcess.Item2.Process;
71+
processlist.Add(new ProcessResult(p, StringMatcher.FuzzySearch(searchTerm, p.ProcessName + p.Id).Score, portNum));
72+
}
6073
return processlist;
6174
}
6275

@@ -80,6 +93,26 @@ public void TryKill(Process p)
8093
catch (Exception e)
8194
{
8295
Log.Exception($"|ProcessKiller.CreateResultsFromProcesses|Failed to kill process {p.ProcessName}", e);
96+
TryKillRunAs(p);
97+
}
98+
}
99+
100+
public void TryKillRunAs(Process p)
101+
{
102+
try
103+
{
104+
Process process = new Process();
105+
ProcessStartInfo startInfo = new ProcessStartInfo();
106+
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
107+
startInfo.FileName = "powershell.exe";
108+
startInfo.Arguments = $"Start cmd.exe -ArgumentList \"/k\",\"taskkill\",\"/f\",\"/pid\", \"{p.Id}\" -Verb Runas";
109+
startInfo.UseShellExecute = false;
110+
process.StartInfo = startInfo;
111+
process.Start();
112+
}
113+
catch(Exception e)
114+
{
115+
Log.Exception($"|ProcessKiller.CreateResultsFromProcesses|Failed to kill process again of run as admin {p.ProcessName}", e);
83116
}
84117
}
85118

Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessResult.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,17 @@ public ProcessResult(Process process, int score)
1010
Score = score;
1111
}
1212

13+
public ProcessResult(Process process, int score, int port)
14+
{
15+
Process = process;
16+
Score = score;
17+
Port = port;
18+
}
19+
1320
public Process Process { get; }
1421

1522
public int Score { get; }
23+
24+
public int Port { set; get; } = 0;
1625
}
1726
}

0 commit comments

Comments
 (0)