From 62dc9166ce95a9dbab9a3e0733b89a6750df92d6 Mon Sep 17 00:00:00 2001
From: ChsBuffer <33744752+chsbuffer@users.noreply.github.com>
Date: Sat, 26 Jun 2021 06:14:08 +0800
Subject: [PATCH] Refactor Guard.cs
---
Netch/Controllers/Guard.cs | 355 +++++++-------------
Netch/Controllers/NTTController.cs | 16 +-
Netch/Controllers/PcapController.cs | 40 +--
Netch/Interfaces/IController.cs | 6 -
Netch/Interfaces/IModeController.cs | 7 +-
Netch/Servers/Shadowsocks/SSController.cs | 17 +-
Netch/Servers/ShadowsocksR/SSRController.cs | 17 +-
Netch/Servers/Trojan/TrojanController.cs | 17 +-
Netch/Servers/V2ray/V2rayController.cs | 26 +-
Netch/Utils/Bandwidth.cs | 26 +-
10 files changed, 195 insertions(+), 332 deletions(-)
diff --git a/Netch/Controllers/Guard.cs b/Netch/Controllers/Guard.cs
index 472dd27a..91be8ebb 100644
--- a/Netch/Controllers/Guard.cs
+++ b/Netch/Controllers/Guard.cs
@@ -1,6 +1,3 @@
-using Netch.Models;
-using Netch.Utils;
-using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -9,83 +6,157 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Netch.Models;
+using Netch.Utils;
using Serilog;
-using Timer = System.Timers.Timer;
namespace Netch.Controllers
{
public abstract class Guard
{
- private readonly Timer _flushFileStreamTimer = new(300) { AutoReset = true };
-
private FileStream? _logFileStream;
-
private StreamWriter? _logStreamWriter;
- private bool _redirectToFile = true;
- ///
- /// 日志文件(重定向输出文件)
- ///
+ /// application path relative of Netch\bin
+ ///
+ /// application output encode
+ protected Guard(string mainFile, bool redirectOutput = true, Encoding? encoding = null)
+ {
+ RedirectOutput = redirectOutput;
+
+ var fileName = Path.GetFullPath($"bin\\{mainFile}");
+
+ if (!File.Exists(fileName))
+ throw new MessageException(i18N.Translate($"bin\\{mainFile} file not found!"));
+
+ Instance = new Process
+ {
+ StartInfo =
+ {
+ FileName = fileName,
+ WorkingDirectory = $"{Global.NetchDir}\\bin",
+ CreateNoWindow = true,
+ UseShellExecute = !RedirectOutput,
+ RedirectStandardOutput = RedirectOutput,
+ StandardOutputEncoding = RedirectOutput ? encoding : null,
+ RedirectStandardError = RedirectOutput,
+ StandardErrorEncoding = RedirectOutput ? encoding : null,
+ WindowStyle = ProcessWindowStyle.Hidden
+ }
+ };
+ }
+
protected string LogPath => Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
- ///
- /// 成功启动关键词
- ///
- protected virtual IEnumerable StartedKeywords { get; set; } = new List();
+ protected virtual IEnumerable StartedKeywords { get; } = new List();
- ///
- /// 启动失败关键词
- ///
- protected virtual IEnumerable StoppedKeywords { get; set; } = new List();
+ protected virtual IEnumerable FailedKeywords { get; } = new List();
public abstract string Name { get; }
- ///
- /// 主程序名
- ///
- public abstract string MainFile { get; protected set; }
+ private State State { get; set; } = State.Waiting;
- protected State State { get; set; } = State.Waiting;
+ private bool RedirectOutput { get; }
- ///
- /// 进程是否可以重定向输出
- ///
- protected bool RedirectStd { get; set; } = true;
+ public Process Instance { get; }
- protected bool RedirectToFile
+ ~Guard()
{
- get => RedirectStd && _redirectToFile;
- set => _redirectToFile = value;
+ _logFileStream?.Dispose();
+ _logStreamWriter?.Dispose();
+ Instance.Dispose();
}
- ///
- /// 进程实例
- ///
- public Process? Instance { get; private set; }
-
- ///
- /// 程序输出的编码,
- ///
- protected virtual Encoding? InstanceOutputEncoding { get; } = null;
-
- public abstract void Stop();
-
- ///
- /// 停止进程
- ///
- protected void StopInstance()
+ protected void StartGuard(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
{
+ State = State.Starting;
+
+ _logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
+ _logStreamWriter = new StreamWriter(_logFileStream) { AutoFlush = true };
+
+ Instance.StartInfo.Arguments = argument;
+ Instance.Start();
+
+ if (priority != ProcessPriorityClass.Normal)
+ Instance.PriorityClass = priority;
+
+ if (RedirectOutput)
+ {
+ Task.Run(() => ReadOutput(Instance.StandardOutput));
+ Task.Run(() => ReadOutput(Instance.StandardError));
+
+ if (!StartedKeywords.Any())
+ {
+ // Skip, No started keyword
+ State = State.Started;
+ return;
+ }
+
+ // wait ReadOutput change State
+ for (var i = 0; i < 1000; i++)
+ {
+ Thread.Sleep(10);
+ switch (State)
+ {
+ case State.Started:
+ OnStarted();
+ return;
+ case State.Stopped:
+ StopGuard();
+ OnStartFailed();
+ throw new MessageException($"{Name} 控制器启动失败");
+ }
+ }
+
+ StopGuard();
+ throw new MessageException($"{Name} 控制器启动超时");
+ }
+ }
+
+ private void ReadOutput(TextReader reader)
+ {
+ string? line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ _logStreamWriter!.WriteLine(line);
+ OnReadNewLine(line);
+
+ if (State == State.Starting)
+ {
+ if (StartedKeywords.Any(s => line.Contains(s)))
+ State = State.Started;
+ else if (FailedKeywords.Any(s => line.Contains(s)))
+ {
+ OnStartFailed();
+ State = State.Stopped;
+ }
+ }
+ }
+
+ State = State.Stopped;
+ }
+
+ public virtual void Stop()
+ {
+ StopGuard();
+ }
+
+ protected void StopGuard()
+ {
+ _logStreamWriter?.Close();
+ _logFileStream?.Close();
+
try
{
- if (Instance == null || Instance.HasExited)
- return;
-
- Instance.Kill();
- Instance.WaitForExit();
+ if (Instance is { HasExited: false })
+ {
+ Instance.Kill();
+ Instance.WaitForExit();
+ }
}
catch (Win32Exception e)
{
- Log.Error(e, "停止 {MainFile} 异常", MainFile);
+ Log.Error(e, "停止 {Name} 异常", Instance.ProcessName);
}
catch
{
@@ -93,191 +164,17 @@ namespace Netch.Controllers
}
}
- ///
- /// 仅初始化 ,不设定事件处理方法
- ///
- ///
- protected virtual void InitInstance(string argument)
+ protected virtual void OnStarted()
{
- Instance = new Process
- {
- StartInfo =
- {
- FileName = Path.GetFullPath($"bin\\{MainFile}"),
- WorkingDirectory = $"{Global.NetchDir}\\bin",
- Arguments = argument,
- CreateNoWindow = true,
- UseShellExecute = !RedirectStd,
- RedirectStandardOutput = RedirectStd,
- StandardOutputEncoding = RedirectStd ? InstanceOutputEncoding : null,
- RedirectStandardError = RedirectStd,
- StandardErrorEncoding = RedirectStd ? InstanceOutputEncoding : null,
- WindowStyle = ProcessWindowStyle.Hidden
- }
- };
-
- if (!File.Exists(Instance.StartInfo.FileName))
- throw new MessageException(i18N.Translate($"bin\\{MainFile} file not found!"));
}
- ///
- /// 默认行为启动主程序
- ///
- /// 主程序启动参数
- /// 进程优先级
- /// 是否成功启动
- protected void StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
- {
- State = State.Starting;
- // 初始化程序
- InitInstance(argument);
-
- if (RedirectToFile)
- OpenLogFile();
-
- // 启动程序
- Instance!.Start();
- if (priority != ProcessPriorityClass.Normal)
- Instance.PriorityClass = priority;
-
- if (RedirectStd)
- {
- Task.Run(() => ReadOutput(Instance.StandardOutput));
- Task.Run(() => ReadOutput(Instance.StandardError));
-
- if (!StartedKeywords.Any())
- {
- State = State.Started;
- return;
- }
- }
- else
- {
- return;
- }
-
- // 等待启动
- for (var i = 0; i < 1000; i++)
- {
- Thread.Sleep(10);
- switch (State)
- {
- case State.Started:
- Task.Run(OnKeywordStarted);
- return;
- case State.Stopped:
- Stop();
- CloseLogFile();
- OnKeywordStopped();
- throw new MessageException($"{Name} 控制器启动失败");
- }
- }
-
- Stop();
- OnKeywordTimeout();
- throw new MessageException($"{Name} 控制器启动超时");
- }
-
- #region FileStream
-
- private void OpenLogFile()
- {
- if (!RedirectToFile)
- return;
-
- _logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
- _logStreamWriter = new StreamWriter(_logFileStream);
-
- _flushFileStreamTimer.Elapsed += FlushFileStreamTimerEvent;
- _flushFileStreamTimer.Enabled = true;
- }
-
- private void WriteLog(string line)
- {
- if (!RedirectToFile)
- return;
-
- _logStreamWriter!.WriteLine(line);
- }
-
- private readonly object _logStreamLock = new();
- private void CloseLogFile()
- {
- if (!RedirectToFile)
- return;
-
- lock (_logStreamLock)
- {
- if (_logFileStream == null)
- return;
-
- _flushFileStreamTimer.Enabled = false;
- _logStreamWriter?.Close();
- _logFileStream?.Close();
- _logStreamWriter = _logStreamWriter = null;
- }
- }
-
- #endregion
-
- #region virtual
-
protected virtual void OnReadNewLine(string line)
{
}
- protected virtual void OnKeywordStarted()
- {
- }
-
- protected virtual void OnKeywordStopped()
+ protected virtual void OnStartFailed()
{
Utils.Utils.Open(LogPath);
}
-
- protected virtual void OnKeywordTimeout()
- {
- }
-
- #endregion
-
- protected void ReadOutput(TextReader reader)
- {
- string? line;
- while ((line = reader.ReadLine()) != null)
- {
- WriteLog(line);
- OnReadNewLine(line);
-
- // State == State.Started if !StartedKeywords.Any()
- if (State == State.Starting)
- {
- if (StartedKeywords.Any(s => line.Contains(s)))
- State = State.Started;
- else if (StoppedKeywords.Any(s => line.Contains(s)))
- State = State.Stopped;
- }
- }
-
- CloseLogFile();
- State = State.Stopped;
- }
-
- ///
- /// 计时器存储日志
- ///
- ///
- ///
- private void FlushFileStreamTimerEvent(object sender, EventArgs e)
- {
- try
- {
- _logStreamWriter!.Flush();
- }
- catch (Exception ex)
- {
- Log.Warning(ex, "写入 {Name} 日志异常", Name);
- }
- }
}
}
\ No newline at end of file
diff --git a/Netch/Controllers/NTTController.cs b/Netch/Controllers/NTTController.cs
index 4f818d13..96f49660 100644
--- a/Netch/Controllers/NTTController.cs
+++ b/Netch/Controllers/NTTController.cs
@@ -10,15 +10,12 @@ namespace Netch.Controllers
{
public class NTTController : Guard, IController
{
- public override string MainFile { get; protected set; } = "NTT.exe";
-
- public override string Name { get; } = "NTT";
-
- public override void Stop()
+ public NTTController() : base("NTT.exe")
{
- StopInstance();
}
+ public override string Name => "NTT";
+
///
/// 启动 NatTypeTester
///
@@ -29,8 +26,8 @@ namespace Netch.Controllers
try
{
- InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
- Instance!.Start();
+ Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
+ Instance.Start();
var output = await Instance.StandardOutput.ReadToEndAsync();
var error = await Instance.StandardError.ReadToEndAsync();
@@ -47,8 +44,7 @@ namespace Netch.Controllers
if (output.IsNullOrWhiteSpace())
if (!error.IsNullOrWhiteSpace())
{
- error = error.Trim();
- var errorFirst = error.Substring(0, error.IndexOf('\n')).Trim();
+ var errorFirst = error.GetLines().First();
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
}
diff --git a/Netch/Controllers/PcapController.cs b/Netch/Controllers/PcapController.cs
index 765cf3e0..500adc6a 100644
--- a/Netch/Controllers/PcapController.cs
+++ b/Netch/Controllers/PcapController.cs
@@ -15,23 +15,27 @@ namespace Netch.Controllers
{
public class PcapController : Guard, IModeController
{
- public override string Name { get; } = "pcap2socks";
+ public PcapController() : base("pcap2socks.exe", encoding: Encoding.UTF8)
+ {
+ _form = new LogForm(Global.MainForm);
+ _form.CreateControl();
+ }
- public override string MainFile { get; protected set; } = "pcap2socks.exe";
+ ~PcapController()
+ {
+ _form.Dispose();
+ }
- protected override IEnumerable StartedKeywords { get; set; } = new[] { "└" };
+ public override string Name => "pcap2socks";
- protected override Encoding? InstanceOutputEncoding { get; } = Encoding.UTF8;
+ protected override IEnumerable StartedKeywords { get; } = new[] { "└" };
- private LogForm? _form;
+ private readonly LogForm _form;
public void Start(in Mode mode)
{
var server = MainController.Server!;
- _form = new LogForm(Global.MainForm);
- _form.CreateControl();
-
var outboundNetworkInterface = NetworkInterfaceUtils.GetBest();
var argument = new StringBuilder($@"-i \Device\NPF_{outboundNetworkInterface.Id}");
@@ -41,26 +45,22 @@ namespace Netch.Controllers
argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}");
argument.Append($" {mode.GetRules().FirstOrDefault() ?? "-P n"}");
- StartInstanceAuto(argument.ToString());
+ StartGuard(argument.ToString());
}
protected override void OnReadNewLine(string line)
{
- Global.MainForm.BeginInvoke(new Action(() =>
- {
- if (!_form!.IsDisposed)
- _form.richTextBox1.AppendText(line + "\n");
- }));
+ Global.MainForm.BeginInvoke(new Action(() => _form.richTextBox1.AppendText(line + "\n")));
}
- protected override void OnKeywordStarted()
+ protected override void OnStarted()
{
- Global.MainForm.BeginInvoke(new Action(() => { _form!.Show(); }));
+ Global.MainForm.BeginInvoke(new Action(() => _form.Show()));
}
- protected override void OnKeywordStopped()
+ protected override void OnStartFailed()
{
- if (File.ReadAllText(LogPath).Length == 0)
+ if (new FileInfo(LogPath).Length == 0)
{
Task.Run(() =>
{
@@ -76,8 +76,8 @@ namespace Netch.Controllers
public override void Stop()
{
- _form!.Close();
- StopInstance();
+ _form.Close();
+ StopGuard();
}
}
}
\ No newline at end of file
diff --git a/Netch/Interfaces/IController.cs b/Netch/Interfaces/IController.cs
index 9d914b02..a7837ec2 100644
--- a/Netch/Interfaces/IController.cs
+++ b/Netch/Interfaces/IController.cs
@@ -2,14 +2,8 @@
{
public interface IController
{
- ///
- /// 控制器名
- ///
public string Name { get; }
- ///
- /// 停止
- ///
public void Stop();
}
}
\ No newline at end of file
diff --git a/Netch/Interfaces/IModeController.cs b/Netch/Interfaces/IModeController.cs
index 76d78b42..8d5c933f 100644
--- a/Netch/Interfaces/IModeController.cs
+++ b/Netch/Interfaces/IModeController.cs
@@ -4,11 +4,6 @@ namespace Netch.Interfaces
{
public interface IModeController : IController
{
- ///
- /// 启动
- ///
- /// 模式
- /// 是否成功
- public abstract void Start(in Mode mode);
+ public void Start(in Mode mode);
}
}
\ No newline at end of file
diff --git a/Netch/Servers/Shadowsocks/SSController.cs b/Netch/Servers/Shadowsocks/SSController.cs
index 6490bdd4..cbd563b8 100644
--- a/Netch/Servers/Shadowsocks/SSController.cs
+++ b/Netch/Servers/Shadowsocks/SSController.cs
@@ -7,13 +7,15 @@ namespace Netch.Servers.Shadowsocks
{
public class SSController : Guard, IServerController
{
- public override string MainFile { get; protected set; } = "Shadowsocks.exe";
+ public SSController() : base("Shadowsocks.exe")
+ {
+ }
- protected override IEnumerable StartedKeywords { get; set; } = new[] { "listening at" };
+ protected override IEnumerable StartedKeywords => new[] { "listening at" };
- protected override IEnumerable StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
+ protected override IEnumerable FailedKeywords => new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
- public override string Name { get; } = "Shadowsocks";
+ public override string Name => "Shadowsocks";
public ushort? Socks5LocalPort { get; set; }
@@ -36,7 +38,7 @@ namespace Netch.Servers.Shadowsocks
plugin_opts = server.PluginOption
};
- StartInstanceAuto(command.ToString());
+ StartGuard(command.ToString());
}
[Verb]
@@ -70,10 +72,5 @@ namespace Netch.Servers.Shadowsocks
[Optional]
public string? acl { get; set; }
}
-
- public override void Stop()
- {
- StopInstance();
- }
}
}
\ No newline at end of file
diff --git a/Netch/Servers/ShadowsocksR/SSRController.cs b/Netch/Servers/ShadowsocksR/SSRController.cs
index 80d9843c..01b5b571 100644
--- a/Netch/Servers/ShadowsocksR/SSRController.cs
+++ b/Netch/Servers/ShadowsocksR/SSRController.cs
@@ -7,13 +7,15 @@ namespace Netch.Servers.ShadowsocksR
{
public class SSRController : Guard, IServerController
{
- public override string MainFile { get; protected set; } = "ShadowsocksR.exe";
+ public SSRController() : base("ShadowsocksR.exe")
+ {
+ }
- protected override IEnumerable StartedKeywords { get; set; } = new[] { "listening at" };
+ protected override IEnumerable StartedKeywords => new[] { "listening at" };
- protected override IEnumerable StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage" };
+ protected override IEnumerable FailedKeywords => new[] { "Invalid config path", "usage" };
- public override string Name { get; } = "ShadowsocksR";
+ public override string Name => "ShadowsocksR";
public ushort? Socks5LocalPort { get; set; }
@@ -39,7 +41,7 @@ namespace Netch.Servers.ShadowsocksR
u = true
};
- StartInstanceAuto(command.ToString());
+ StartGuard(command.ToString());
}
[Verb]
@@ -79,10 +81,5 @@ namespace Netch.Servers.ShadowsocksR
[Optional]
public string? acl { get; set; }
}
-
- public override void Stop()
- {
- StopInstance();
- }
}
}
\ No newline at end of file
diff --git a/Netch/Servers/Trojan/TrojanController.cs b/Netch/Servers/Trojan/TrojanController.cs
index 69678730..0b3fc262 100644
--- a/Netch/Servers/Trojan/TrojanController.cs
+++ b/Netch/Servers/Trojan/TrojanController.cs
@@ -10,13 +10,15 @@ namespace Netch.Servers.Trojan
{
public class TrojanController : Guard, IServerController
{
- public override string MainFile { get; protected set; } = "Trojan.exe";
+ public TrojanController() : base("Trojan.exe")
+ {
+ }
- protected override IEnumerable StartedKeywords { get; set; } = new[] { "started" };
+ protected override IEnumerable StartedKeywords => new[] { "started" };
- protected override IEnumerable StoppedKeywords { get; set; } = new[] { "exiting" };
+ protected override IEnumerable FailedKeywords => new[] { "exiting" };
- public override string Name { get; } = "Trojan";
+ public override string Name => "Trojan";
public ushort? Socks5LocalPort { get; set; }
@@ -45,12 +47,7 @@ namespace Netch.Servers.Trojan
File.WriteAllBytes(Constants.TempConfig, JsonSerializer.SerializeToUtf8Bytes(trojanConfig, Global.NewDefaultJsonSerializerOptions));
- StartInstanceAuto("-c ..\\data\\last.json");
- }
-
- public override void Stop()
- {
- StopInstance();
+ StartGuard("-c ..\\data\\last.json");
}
}
}
\ No newline at end of file
diff --git a/Netch/Servers/V2ray/V2rayController.cs b/Netch/Servers/V2ray/V2rayController.cs
index 134d0aad..ba0686d0 100644
--- a/Netch/Servers/V2ray/V2rayController.cs
+++ b/Netch/Servers/V2ray/V2rayController.cs
@@ -9,13 +9,17 @@ namespace Netch.Servers.V2ray
{
public class V2rayController : Guard, IServerController
{
- public override string MainFile { get; protected set; } = "xray.exe";
+ public V2rayController() : base("xray.exe")
+ {
+ if (!Global.Settings.V2RayConfig.XrayCone)
+ Instance.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
+ }
- protected override IEnumerable StartedKeywords { get; set; } = new[] { "started" };
+ protected override IEnumerable StartedKeywords => new[] { "started" };
- protected override IEnumerable StoppedKeywords { get; set; } = new[] { "config file not readable", "failed to" };
+ protected override IEnumerable FailedKeywords => new[] { "config file not readable", "failed to" };
- public override string Name { get; } = "Xray";
+ public override string Name => "Xray";
public ushort? Socks5LocalPort { get; set; }
@@ -24,19 +28,7 @@ namespace Netch.Servers.V2ray
public virtual void Start(in Server s, in Mode mode)
{
File.WriteAllText(Constants.TempConfig, V2rayConfigUtils.GenerateClientConfig(s, mode));
- StartInstanceAuto("-config ..\\data\\last.json");
- }
-
- public override void Stop()
- {
- StopInstance();
- }
-
- protected override void InitInstance(string argument)
- {
- base.InitInstance(argument);
- if (!Global.Settings.V2RayConfig.XrayCone)
- Instance!.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
+ StartGuard("-config ..\\data\\last.json");
}
}
}
\ No newline at end of file
diff --git a/Netch/Utils/Bandwidth.cs b/Netch/Utils/Bandwidth.cs
index 54e3d1e8..ffcf095e 100644
--- a/Netch/Utils/Bandwidth.cs
+++ b/Netch/Utils/Bandwidth.cs
@@ -1,12 +1,12 @@
-using Microsoft.Diagnostics.Tracing.Parsers;
-using Microsoft.Diagnostics.Tracing.Session;
-using Netch.Controllers;
-using Netch.Models;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Diagnostics.Tracing.Parsers;
+using Microsoft.Diagnostics.Tracing.Session;
+using Netch.Controllers;
+using Netch.Models;
using Serilog;
namespace Netch.Utils
@@ -58,9 +58,8 @@ namespace Netch.Utils
{
case null:
break;
- case Guard instanceController:
- if (instanceController.Instance != null)
- instances.Add(instanceController.Instance);
+ case Guard guard:
+ instances.Add(guard.Instance);
break;
}
@@ -70,18 +69,17 @@ namespace Netch.Utils
{
case null:
break;
- case NFController _:
+ case NFController:
instances.Add(Process.GetCurrentProcess());
break;
- case Guard instanceController:
- instances.Add(instanceController.Instance!);
+ case Guard guard:
+ instances.Add(guard.Instance);
break;
}
- var processList = instances.Select(instance => instance.Id).ToList();
+ var processList = instances.Select(instance => instance.Id).ToHashSet();
- Log.Information("流量统计进程: {Processes}",
- $"{string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())}");
+ Log.Information("流量统计进程: {Processes}", string.Join(',', instances.Select(v => $"({v.Id}){v.ProcessName}")));
received = 0;