mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
19 Commits
1.4.12-Bet
...
1.4.12-Bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a15fa7375 | ||
|
|
e10c994e38 | ||
|
|
54a263ad06 | ||
|
|
8a56dd4582 | ||
|
|
cd891cec33 | ||
|
|
635a033434 | ||
|
|
6fd3aa48a5 | ||
|
|
a319833bd5 | ||
|
|
70e5d8324e | ||
|
|
1cecccb173 | ||
|
|
281c67aced | ||
|
|
a779295525 | ||
|
|
e665850fc9 | ||
|
|
2537fdd8fe | ||
|
|
cc459d3f59 | ||
|
|
7c051413d3 | ||
|
|
17165e4623 | ||
|
|
a6fd0764e1 | ||
|
|
d27c7c016c |
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
@@ -8,10 +7,8 @@ namespace Netch.Controllers
|
||||
{
|
||||
public DNSController()
|
||||
{
|
||||
AkaName = "dns Service";
|
||||
MainFile = "unbound";
|
||||
ExtFiles = new[] {"unbound-service.conf", "forward-zone.conf"};
|
||||
InitCheck();
|
||||
Name = "DNS Service";
|
||||
MainFile = "unbound.exe";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -20,9 +17,7 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public bool Start()
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
Instance = GetProcess("bin\\unbound.exe");
|
||||
Instance = GetProcess();
|
||||
Instance.StartInfo.Arguments = "-c unbound-service.conf -v";
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
@@ -42,11 +37,6 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
WriteLog(e);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
@@ -10,8 +9,10 @@ namespace Netch.Controllers
|
||||
{
|
||||
public SSController()
|
||||
{
|
||||
MainFile = "Shadowsocks";
|
||||
InitCheck();
|
||||
Name = "Shadowsocks";
|
||||
MainFile = "Shadowsocks.exe";
|
||||
StartedKeywords("listening at");
|
||||
StoppedKeywords("Invalid config path","usage","plugin service exit unexpectedly");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
@@ -45,8 +46,7 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Ready) return false;
|
||||
Instance = GetProcess("bin\\Shadowsocks.exe");
|
||||
Instance = GetProcess();
|
||||
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u";
|
||||
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
|
||||
@@ -89,18 +89,5 @@ namespace Netch.Controllers
|
||||
else
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("listening at"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage") || e.Data.Contains("plugin service exit unexpectedly")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -9,16 +8,15 @@ namespace Netch.Controllers
|
||||
{
|
||||
public SSRController()
|
||||
{
|
||||
MainFile = "ShadowsocksR";
|
||||
InitCheck();
|
||||
Name = "ShadowsocksR";
|
||||
MainFile = "ShadowsocksR.exe";
|
||||
StartedKeywords("listening at");
|
||||
StoppedKeywords("Invalid config path","usage");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
Instance = GetProcess("bin\\ShadowsocksR.exe");
|
||||
Instance.StartInfo.FileName = "bin\\ShadowsocksR.exe";
|
||||
Instance = GetProcess();
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
@@ -67,19 +65,6 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("listening at"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
@@ -12,13 +11,14 @@ namespace Netch.Controllers
|
||||
{
|
||||
public TrojanController()
|
||||
{
|
||||
MainFile = "Trojan";
|
||||
InitCheck();
|
||||
Name = "Trojan";
|
||||
MainFile = "Trojan.exe";
|
||||
StartedKeywords("started");
|
||||
StoppedKeywords("exiting");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
|
||||
{
|
||||
@@ -32,7 +32,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
}));
|
||||
|
||||
Instance = GetProcess("bin\\Trojan.exe");
|
||||
Instance = GetProcess();
|
||||
Instance.StartInfo.Arguments = "-c ..\\data\\last.json";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
@@ -61,19 +61,6 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("exiting")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
@@ -13,13 +12,14 @@ namespace Netch.Controllers
|
||||
{
|
||||
public VMessController()
|
||||
{
|
||||
MainFile = "v2ray";
|
||||
InitCheck();
|
||||
Name = "V2Ray";
|
||||
MainFile = "v2ray.exe";
|
||||
StartedKeywords("started");
|
||||
StoppedKeywords("config file not readable", "failed to");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new VMess.Config
|
||||
{
|
||||
inbounds = new List<VMess.Inbounds>
|
||||
@@ -58,14 +58,14 @@ namespace Netch.Controllers
|
||||
streamSettings = new VMess.StreamSettings
|
||||
{
|
||||
network = server.TransferProtocol,
|
||||
security = server.TLSSecure ? "tls" : "",
|
||||
security = server.TLSSecure ? "tls" : string.Empty,
|
||||
wsSettings = server.TransferProtocol == "ws"
|
||||
? new VMess.WebSocketSettings
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMess.WSHeaders
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
: null,
|
||||
@@ -77,10 +77,10 @@ namespace Netch.Controllers
|
||||
type = server.FakeType,
|
||||
request = new VMess.TCPRequest
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMess.TCPRequestHeaders
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
}));
|
||||
|
||||
Instance = GetProcess("bin\\v2ray.exe");
|
||||
Instance = GetProcess();
|
||||
Instance.StartInfo.Arguments = "-config ..\\data\\last.json";
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
@@ -203,19 +203,6 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("config file not readable") || e.Data.Contains("failed to")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Netch.Models;
|
||||
@@ -10,15 +11,8 @@ namespace Netch.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 控制器名
|
||||
/// <param />
|
||||
/// 未赋值会在 <see cref="InitCheck" /> 赋值为 <see cref="MainFile" />
|
||||
/// </summary>
|
||||
public string AkaName;
|
||||
|
||||
/// <summary>
|
||||
/// 其他需要文件
|
||||
/// </summary>
|
||||
protected string[] ExtFiles;
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
@@ -30,10 +24,29 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public string MainFile;
|
||||
|
||||
/// <summary>
|
||||
/// 运行检查, 由 <see cref="InitCheck()" /> 赋值
|
||||
/// </summary>
|
||||
public bool Ready;
|
||||
private List<string> _startedKeywords = new List<string>();
|
||||
|
||||
private List<string> _stoppedKeywords = new List<string>();
|
||||
|
||||
protected bool RedirectStd = true;
|
||||
|
||||
protected void StartedKeywords(params string[] texts)
|
||||
{
|
||||
foreach (var text in texts)
|
||||
{
|
||||
_startedKeywords.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void StoppedKeywords(params string[] texts)
|
||||
{
|
||||
foreach (var text in texts)
|
||||
{
|
||||
_stoppedKeywords.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
@@ -55,96 +68,102 @@ namespace Netch.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"停止 {MainFile}.exe 错误:\n" + e);
|
||||
Logging.Error($"停止 {MainFile} 错误:\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 杀残留进程,清除日志,检查文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected void InitCheck()
|
||||
public void ClearLog()
|
||||
{
|
||||
if (string.IsNullOrEmpty(AkaName)) AkaName = MainFile;
|
||||
|
||||
var result = false;
|
||||
// 杀残留
|
||||
MainController.KillProcessByName(MainFile);
|
||||
// 清日志
|
||||
try
|
||||
{
|
||||
if (File.Exists($"logging\\{AkaName}.log")) File.Delete($"logging\\{AkaName}.log");
|
||||
if (File.Exists($"logging\\{Name}.log")) File.Delete($"logging\\{Name}.log");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
// 检查文件
|
||||
var mainResult = true;
|
||||
var extResult = true;
|
||||
if (!string.IsNullOrEmpty(MainFile) && !File.Exists($"bin\\{MainFile}.exe"))
|
||||
{
|
||||
mainResult = false;
|
||||
Logging.Error($"主程序 bin\\{MainFile}.exe 不存在");
|
||||
}
|
||||
|
||||
if (ExtFiles == null)
|
||||
extResult = true;
|
||||
else
|
||||
foreach (var f in ExtFiles)
|
||||
if (!File.Exists($"bin\\{f}"))
|
||||
{
|
||||
extResult = false;
|
||||
Logging.Error($"附加文件 bin\\{f} 不存在");
|
||||
}
|
||||
|
||||
result = extResult && mainResult;
|
||||
if (!result)
|
||||
Logging.Error(AkaName + " 未就绪");
|
||||
Ready = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写日志
|
||||
/// </summary>
|
||||
/// <param name="std"></param>
|
||||
/// <returns><see cref="std" />是否为空</returns>
|
||||
protected bool WriteLog(DataReceivedEventArgs std)
|
||||
/// <param name="s"></param>
|
||||
/// <returns><see cref="s" />是否为空</returns>
|
||||
protected bool Write(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(std.Data)) return false;
|
||||
if (string.IsNullOrWhiteSpace(s)) return false;
|
||||
try
|
||||
|
||||
{
|
||||
File.AppendAllText($"logging\\{AkaName}.log", $@"{std.Data}{Global.EOF}");
|
||||
File.AppendAllText($"logging\\{Name}.log", s + Global.EOF);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"写入{AkaName}日志错误:\n" + e);
|
||||
Logging.Error($"写入{Name}日志错误:\n" + e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Process GetProcess(string path = null, bool redirectStd = true)
|
||||
public Process GetProcess()
|
||||
{
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
Arguments = "",
|
||||
FileName = Path.GetFullPath($"bin\\{MainFile}"),
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = redirectStd,
|
||||
RedirectStandardInput = redirectStd,
|
||||
RedirectStandardOutput = redirectStd,
|
||||
RedirectStandardError = RedirectStd,
|
||||
RedirectStandardInput = RedirectStd,
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
UseShellExecute = false
|
||||
},
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
if (path != null) p.StartInfo.FileName = Path.GetFullPath(path);
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 接收输出数据
|
||||
/// </summary>
|
||||
/// <param name="sender">发送者</param>
|
||||
/// <param name="e">数据</param>
|
||||
protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
// 写入日志
|
||||
if (!Write(e.Data)) return;
|
||||
|
||||
// 检查启动
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
{
|
||||
State = State.Stopped;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var s in _startedKeywords)
|
||||
{
|
||||
if (e.Data.Contains(s))
|
||||
{
|
||||
State = State.Started;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var s in _stoppedKeywords)
|
||||
{
|
||||
if (e.Data.Contains(s))
|
||||
{
|
||||
State = State.Stopped;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using Netch.Models;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -12,7 +11,5 @@ namespace Netch.Controllers
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public abstract bool Start(Server server, Mode mode);
|
||||
|
||||
public abstract void OnOutputDataReceived(object sender, DataReceivedEventArgs e);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -11,6 +12,12 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class MainController
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前使用的端口
|
||||
/// <see cref="MainForm.LocalPortText"/>
|
||||
/// </summary>
|
||||
public static readonly List<int> UsingPorts = new List<int>();
|
||||
|
||||
public EncryptedProxy pEncryptedProxyController;
|
||||
|
||||
public ModeController pModeController;
|
||||
@@ -18,7 +25,7 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// NTT 控制器
|
||||
/// </summary>
|
||||
public NTTController pNTTController;
|
||||
public NTTController pNTTController = new NTTController();
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
|
||||
public static extern uint FlushDNSResolverCache();
|
||||
@@ -31,7 +38,6 @@ namespace Netch.Controllers
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Server server, Mode mode)
|
||||
{
|
||||
pNTTController ??= new NTTController();
|
||||
FlushDNSResolverCache();
|
||||
|
||||
var result = false;
|
||||
@@ -57,13 +63,34 @@ namespace Netch.Controllers
|
||||
break;
|
||||
}
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.AkaName));
|
||||
if (pEncryptedProxyController.Ready) result = pEncryptedProxyController.Start(server, mode);
|
||||
KillProcessByName(pEncryptedProxyController.MainFile);
|
||||
|
||||
// 检查端口是否被占用
|
||||
if (PortHelper.PortInUse(Global.Settings.Socks5LocalPort))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", "Socks5"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PortHelper.PortInUse(Global.Settings.HTTPLocalPort))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", "HTTP"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PortHelper.PortInUse(Global.Settings.RedirectorTCPPort, PortType.TCP))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", "Redirector TCP"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name));
|
||||
result = pEncryptedProxyController.Start(server, mode);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
// 加密代理已启动
|
||||
Logging.Info("加密代理已启动");
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0: // 进程代理模式
|
||||
@@ -82,28 +109,30 @@ namespace Netch.Controllers
|
||||
break;
|
||||
}
|
||||
|
||||
if (pModeController != null && pModeController.Ready)
|
||||
if (pModeController != null)
|
||||
{
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting ", pModeController.AkaName));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name));
|
||||
result = pModeController.Start(server, mode);
|
||||
}
|
||||
|
||||
switch (mode.Type)
|
||||
if (result)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
if (result)
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
Task.Run(() =>
|
||||
{
|
||||
MainForm.Instance.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
// Thread.Sleep(1000);
|
||||
var (nttResult, natType, localEnd, publicEnd) = pNTTController.Start();
|
||||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||||
|
||||
if (nttResult) MainForm.Instance.NatTypeStatusText(natType, country);
|
||||
if (nttResult) Global.MainForm.NatTypeStatusText(natType, country);
|
||||
});
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +146,8 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
pEncryptedProxyController?.Stop();
|
||||
Task.Run(() => pEncryptedProxyController?.Stop());
|
||||
Task.Run(() => UsingPorts.Clear());
|
||||
pModeController?.Stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Netch.Controllers
|
||||
|
||||
public HTTPController()
|
||||
{
|
||||
AkaName = "HTTP";
|
||||
Ready = true;
|
||||
Name = "HTTP";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,8 +30,6 @@ namespace Netch.Controllers
|
||||
/// <returns>是否启动成功</returns>
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
RecordPrevious();
|
||||
try
|
||||
{
|
||||
|
||||
@@ -3,8 +3,6 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using nfapinet;
|
||||
@@ -15,19 +13,13 @@ namespace Netch.Controllers
|
||||
{
|
||||
private static readonly ServiceController NFService = new ServiceController("netfilter2");
|
||||
|
||||
private static readonly string BinDriver = "";
|
||||
private static readonly string BinDriver = string.Empty;
|
||||
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
|
||||
|
||||
public static string DriverVersion(string file)
|
||||
{
|
||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : "";
|
||||
}
|
||||
private static string[] _sysDns = { };
|
||||
|
||||
static NFController()
|
||||
{
|
||||
// 生成系统版本
|
||||
var winNTver = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
|
||||
switch (winNTver)
|
||||
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
|
||||
{
|
||||
case "10.0":
|
||||
BinDriver = "Win-10.sys";
|
||||
@@ -41,7 +33,7 @@ namespace Netch.Controllers
|
||||
BinDriver = "Win-7.sys";
|
||||
break;
|
||||
default:
|
||||
Logging.Error($"不支持的系统版本:{winNTver}");
|
||||
Logging.Error($"不支持的系统版本:{Environment.OSVersion.Version}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,22 +42,24 @@ namespace Netch.Controllers
|
||||
|
||||
public NFController()
|
||||
{
|
||||
MainFile = "Redirector";
|
||||
ExtFiles = new[] {Path.GetFileName(BinDriver)};
|
||||
InitCheck();
|
||||
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
InstallDriver();
|
||||
}
|
||||
Name = "Redirector";
|
||||
MainFile = "Redirector.exe";
|
||||
StartedKeywords("Started");
|
||||
StoppedKeywords("Failed", "Unable");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!CheckDriverReady())
|
||||
Logging.Info("内置驱动版本: " + DriverVersion(BinDriver));
|
||||
if (DriverVersion(SystemDriver) != DriverVersion(BinDriver))
|
||||
{
|
||||
if (File.Exists(SystemDriver))
|
||||
{
|
||||
Logging.Info("系统驱动版本: " + DriverVersion(SystemDriver));
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
}
|
||||
|
||||
if (!InstallDriver())
|
||||
return false;
|
||||
}
|
||||
@@ -75,7 +69,7 @@ namespace Netch.Controllers
|
||||
processList += proc + ",";
|
||||
processList += "NTT.exe";
|
||||
|
||||
Instance = GetProcess("bin\\Redirector.exe");
|
||||
Instance = GetProcess();
|
||||
if (server.Type != "Socks5")
|
||||
{
|
||||
Instance.StartInfo.Arguments += $"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processList}\"";
|
||||
@@ -109,10 +103,19 @@ namespace Netch.Controllers
|
||||
{
|
||||
Thread.Sleep(250);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
if (State == State.Started)
|
||||
{
|
||||
|
||||
//备份并替换系统DNS
|
||||
_sysDns = DNS.getSystemDns();
|
||||
string[] dns = {"1.1.1.1", "8.8.8.8"};
|
||||
DNS.SetDNS(dns);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Error("NF 进程启动超时");
|
||||
Logging.Error(Name + " 启动超时");
|
||||
Stop();
|
||||
if (!RestartService()) return false;
|
||||
}
|
||||
@@ -131,11 +134,11 @@ namespace Netch.Controllers
|
||||
// 防止其他程序占用 重置 NF 百万连接数限制
|
||||
NFService.Stop();
|
||||
NFService.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
NFService.Start();
|
||||
break;
|
||||
case ServiceControllerStatus.Stopped:
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
NFService.Start();
|
||||
break;
|
||||
}
|
||||
@@ -157,17 +160,19 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CheckDriverReady()
|
||||
public static string DriverVersion(string file)
|
||||
{
|
||||
// 检查驱动是否存在
|
||||
if (!File.Exists(SystemDriver)) return false;
|
||||
|
||||
// 检查驱动版本号
|
||||
return DriverVersion(SystemDriver) == DriverVersion(BinDriver);
|
||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载 NF 驱动
|
||||
/// </summary>
|
||||
/// <returns>是否成功卸载</returns>
|
||||
public static bool UninstallDriver()
|
||||
{
|
||||
Global.MainForm.StatusText("Uninstall netfilter2");
|
||||
Logging.Info("卸载NF驱动");
|
||||
try
|
||||
{
|
||||
if (NFService.Status == ServiceControllerStatus.Running)
|
||||
@@ -182,22 +187,28 @@ namespace Netch.Controllers
|
||||
}
|
||||
|
||||
if (!File.Exists(SystemDriver)) return true;
|
||||
|
||||
try
|
||||
{
|
||||
NFAPI.nf_unRegisterDriver("netfilter2");
|
||||
|
||||
File.Delete(SystemDriver);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw ex;
|
||||
Logging.Error(e.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
File.Delete(SystemDriver);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安装 NF 驱动
|
||||
/// </summary>
|
||||
/// <returns>驱动是否安装成功</returns>
|
||||
public static bool InstallDriver()
|
||||
{
|
||||
Logging.Info("安装驱动中");
|
||||
Logging.Info("安装NF驱动");
|
||||
try
|
||||
{
|
||||
File.Copy(BinDriver, SystemDriver);
|
||||
@@ -208,12 +219,12 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Register driver"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Register driver"));
|
||||
// 注册驱动文件
|
||||
var result = NFAPI.nf_registerDriver("netfilter2");
|
||||
if (result == NF_STATUS.NF_STATUS_SUCCESS)
|
||||
{
|
||||
Logging.Info($"驱动安装成功,当前驱动版本:{DriverVersion(DriverVersion(SystemDriver))}");
|
||||
Logging.Info($"驱动安装成功,当前驱动版本:{DriverVersion(SystemDriver)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -224,38 +235,40 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("Started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Failed") || e.Data.Contains("Unable")) State = State.Stopped;
|
||||
}
|
||||
else if (State == State.Started)
|
||||
{
|
||||
if (e.Data.StartsWith("[APP][Bandwidth]"))
|
||||
{
|
||||
var splited = e.Data.Replace("[APP][Bandwidth]", "").Trim().Split(',');
|
||||
if (splited.Length == 2)
|
||||
{
|
||||
var uploadSplited = splited[0].Split(':');
|
||||
var downloadSplited = splited[1].Split(':');
|
||||
|
||||
if (uploadSplited.Length == 2 && downloadSplited.Length == 2)
|
||||
if (long.TryParse(uploadSplited[1], out var upload) && long.TryParse(downloadSplited[1], out var download))
|
||||
Task.Run(() => OnBandwidthUpdated(upload, download));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
// {
|
||||
// if (!Write(e.Data)) return;
|
||||
// if (State == State.Starting)
|
||||
// {
|
||||
// if (Instance.HasExited)
|
||||
// State = State.Stopped;
|
||||
// else if (e.Data.Contains("Started"))
|
||||
// State = State.Started;
|
||||
// else if (e.Data.Contains("Failed") || e.Data.Contains("Unable")) State = State.Stopped;
|
||||
// }
|
||||
// else if (State == State.Started)
|
||||
// {
|
||||
// if (e.Data.StartsWith("[APP][Bandwidth]"))
|
||||
// {
|
||||
// var splited = e.Data.Replace("[APP][Bandwidth]", "").Trim().Split(',');
|
||||
// if (splited.Length == 2)
|
||||
// {
|
||||
// var uploadSplited = splited[0].Split(':');
|
||||
// var downloadSplited = splited[1].Split(':');
|
||||
//
|
||||
// if (uploadSplited.Length == 2 && downloadSplited.Length == 2)
|
||||
// if (long.TryParse(uploadSplited[1], out var upload) && long.TryParse(downloadSplited[1], out var download))
|
||||
// Task.Run(() => OnBandwidthUpdated(upload, download));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
//恢复系统DNS
|
||||
DNS.SetDNS(_sysDns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
@@ -35,14 +34,16 @@ namespace Netch.Controllers
|
||||
|
||||
public TUNTAPController()
|
||||
{
|
||||
MainFile = "tun2socks";
|
||||
InitCheck();
|
||||
Name = "Tap";
|
||||
MainFile = "tun2socks.exe";
|
||||
StartedKeywords("Running");
|
||||
StoppedKeywords("failed","invalid vconfig file");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置 TUNTAP 适配器
|
||||
/// </summary>
|
||||
public bool Configure()
|
||||
private bool Configure()
|
||||
{
|
||||
// 查询服务器 IP 地址
|
||||
var destination = Dns.GetHostAddressesAsync(_savedServer.Hostname);
|
||||
@@ -60,9 +61,9 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 设置绕行规则
|
||||
/// </summary>
|
||||
public bool SetupBypass()
|
||||
private bool SetupBypass()
|
||||
{
|
||||
MainForm.Instance.StatusText(i18N.Translate("SetupBypass"));
|
||||
Global.MainForm.StatusText(i18N.Translate("SetupBypass"));
|
||||
Logging.Info("设置绕行规则 → 设置让服务器 IP 走直连");
|
||||
// 让服务器 IP 走直连
|
||||
foreach (var address in _serverAddresses)
|
||||
@@ -178,7 +179,7 @@ namespace Netch.Controllers
|
||||
Logging.Info("设置绕行规则 → 处理自定义 DNS 代理");
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
var dns = "";
|
||||
var dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
@@ -252,7 +253,7 @@ namespace Netch.Controllers
|
||||
{
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
var dns = "";
|
||||
var dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
@@ -308,9 +309,7 @@ namespace Netch.Controllers
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting Tap"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting Tap"));
|
||||
|
||||
_savedMode = mode;
|
||||
_savedServer = server;
|
||||
@@ -321,7 +320,7 @@ namespace Netch.Controllers
|
||||
SetupBypass();
|
||||
Logging.Info("设置绕行规则完毕");
|
||||
|
||||
Instance = GetProcess("bin\\tun2socks.exe");
|
||||
Instance = GetProcess();
|
||||
|
||||
var adapterName = TUNTAP.GetName(Global.TUNTAP.ComponentID);
|
||||
Logging.Info($"tun2sock使用适配器:{adapterName}");
|
||||
@@ -331,7 +330,7 @@ namespace Netch.Controllers
|
||||
//if (Global.Settings.TUNTAP.UseCustomDNS || server.Type.Equals("VMess"))
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
dns = "";
|
||||
dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
@@ -389,17 +388,6 @@ namespace Netch.Controllers
|
||||
pDNSController.Stop();
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (e.Data.Contains("Running"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("failed") || e.Data.Contains("invalid vconfig file")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜索出口
|
||||
/// </summary>
|
||||
@@ -431,7 +419,7 @@ namespace Netch.Controllers
|
||||
// 通过索引查找对应适配器的 IPv4 地址
|
||||
if (p.Index == Global.Adapter.Index)
|
||||
{
|
||||
var AdapterIPs = "";
|
||||
var AdapterIPs = string.Empty;
|
||||
|
||||
foreach (var ip in adapterProperties.UnicastAddresses)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace Netch.Controllers
|
||||
|
||||
public NTTController()
|
||||
{
|
||||
MainFile = "NTT";
|
||||
InitCheck();
|
||||
Name = "NTT";
|
||||
MainFile = "NTT.exe";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,10 +21,9 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public (bool, string, string, string) Start()
|
||||
{
|
||||
if (!Ready) return (false, null, null, null);
|
||||
try
|
||||
{
|
||||
Instance = GetProcess("bin\\NTT.exe");
|
||||
Instance = GetProcess();
|
||||
|
||||
Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
|
||||
|
||||
@@ -52,7 +51,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
_lastResult = e.Data;
|
||||
|
||||
@@ -9,15 +9,13 @@ namespace Netch.Controllers
|
||||
{
|
||||
public PrivoxyController()
|
||||
{
|
||||
MainFile = "Privoxy";
|
||||
ExtFiles = new[] {"default.conf"};
|
||||
InitCheck();
|
||||
Name = "Privoxy";
|
||||
MainFile = "Privoxy.exe";
|
||||
RedirectStd = false;
|
||||
}
|
||||
|
||||
public bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
var isSocks5 = server.Type == "Socks5";
|
||||
var socks5Port = isSocks5 ? server.Port : Global.Settings.Socks5LocalPort;
|
||||
var text = File.ReadAllText("bin\\default.conf")
|
||||
@@ -28,7 +26,7 @@ namespace Netch.Controllers
|
||||
text = text.Replace("/ 127.0.0.1", $"/ {server.Hostname}");
|
||||
File.WriteAllText("data\\privoxy.conf", text);
|
||||
|
||||
Instance = GetProcess("bin\\Privoxy.exe", false);
|
||||
Instance = GetProcess();
|
||||
Instance.StartInfo.Arguments = "..\\data\\privoxy.conf";
|
||||
Instance.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
Instance.StartInfo.UseShellExecute = true;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.Message);
|
||||
Debug.WriteLine(e.ToString());
|
||||
if (notifyNoFound) NewVersionFoundFailed?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,13 @@ namespace Netch.Forms
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
public void ControlFun()
|
||||
private bool _isFirstCloseWindow = true;
|
||||
|
||||
private void ControlFun()
|
||||
{
|
||||
SaveConfigs();
|
||||
//防止模式选择框变成蓝色:D
|
||||
ModeComboBox.Select(0, 0);
|
||||
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
{
|
||||
// 服务器、模式 需选择
|
||||
@@ -29,49 +33,34 @@ namespace Netch.Forms
|
||||
|
||||
if (ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
return;
|
||||
}
|
||||
|
||||
//MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = false;
|
||||
|
||||
UpdateStatus(State.Starting);
|
||||
|
||||
Firewall.AddNetchFwRules();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Task.Run(Firewall.AddNetchFwRules);
|
||||
|
||||
var server = ServerComboBox.SelectedItem as Models.Server;
|
||||
var mode = ModeComboBox.SelectedItem as Models.Mode;
|
||||
|
||||
MainController ??= new MainController();
|
||||
|
||||
var startResult = MainController.Start(server, mode);
|
||||
|
||||
if (startResult)
|
||||
if (_mainController.Start(server, mode))
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
UpdateStatus(State.Started);
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(State)) + PortText(server.Type,mode.Type));
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
//LastDownloadBandwidth = 0;
|
||||
//UploadSpeedLabel.Text = "↑: 0 KB/s";
|
||||
DownloadSpeedLabel.Text = "↑↓: 0 KB/s";
|
||||
UsedBandwidthLabel.Text = $"{i18N.Translate("Used",": ")}0 KB";
|
||||
UsedBandwidthLabel.Visible = UploadSpeedLabel.Visible = DownloadSpeedLabel.Visible = true;
|
||||
UploadSpeedLabel.Visible = false;
|
||||
Bandwidth.NetTraffic(server, mode, MainController);
|
||||
UpdateStatus(State.Started,
|
||||
i18N.Translate(StateExtension.GetStatusString(State.Started)) + LocalPortText(server.Type, mode.Type));
|
||||
Bandwidth.NetTraffic(server, mode, _mainController);
|
||||
});
|
||||
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
{
|
||||
WindowState = FormWindowState.Minimized;
|
||||
NotifyIcon.Visible = true;
|
||||
|
||||
if (IsFirstOpened)
|
||||
if (_isFirstCloseWindow)
|
||||
{
|
||||
// 显示提示语
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
@@ -80,7 +69,7 @@ namespace Netch.Forms
|
||||
"Netch is now minimized to the notification bar, double click this icon to restore."),
|
||||
ToolTipIcon.Info);
|
||||
|
||||
IsFirstOpened = false;
|
||||
_isFirstCloseWindow = false;
|
||||
}
|
||||
|
||||
Hide();
|
||||
@@ -111,8 +100,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatus(State.Stopped);
|
||||
StatusText(i18N.Translate("Start failed"));
|
||||
UpdateStatus(State.Stopped, i18N.Translate("Start failed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -120,57 +108,53 @@ namespace Netch.Forms
|
||||
{
|
||||
// 停止
|
||||
UpdateStatus(State.Stopping);
|
||||
MainController.Stop();
|
||||
_mainController.Stop();
|
||||
UpdateStatus(State.Stopped);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
TestServer();
|
||||
});
|
||||
Task.Run(TestServer);
|
||||
}
|
||||
}
|
||||
|
||||
private string PortText(string serverType,int modeType)
|
||||
private static string LocalPortText(string serverType, int modeType)
|
||||
{
|
||||
var text = new StringBuilder(" (");
|
||||
text.Append(Global.Settings.LocalAddress == "0.0.0.0"
|
||||
? i18N.Translate("Allow other Devices to connect") + " "
|
||||
: "");
|
||||
if (Global.Settings.LocalAddress == "0.0.0.0")
|
||||
text.Append(i18N.Translate("Allow other Devices to connect") + " ");
|
||||
if (serverType == "Socks5")
|
||||
{
|
||||
// 不可控Socks5
|
||||
if (modeType == 3 || modeType == 5)
|
||||
{
|
||||
// 可控HTTP
|
||||
text.Append(
|
||||
$"HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
MainController.UsingPorts.Add(Global.Settings.HTTPLocalPort);
|
||||
text.Append($"HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不可控HTTP
|
||||
text.Clear();
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 可控Socks5
|
||||
text.Append(
|
||||
$"Socks5 {i18N.Translate("Local Port", ": ")}{Global.Settings.Socks5LocalPort}");
|
||||
MainController.UsingPorts.Add(Global.Settings.Socks5LocalPort);
|
||||
text.Append($"Socks5 {i18N.Translate("Local Port", ": ")}{Global.Settings.Socks5LocalPort}");
|
||||
if (modeType == 3 || modeType == 5)
|
||||
{
|
||||
//有HTTP
|
||||
text.Append(
|
||||
$" | HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
// 有HTTP
|
||||
MainController.UsingPorts.Add(Global.Settings.HTTPLocalPort);
|
||||
text.Append($" | HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
}
|
||||
}
|
||||
if (text.Length > 0)
|
||||
{
|
||||
text.Append(")");
|
||||
}
|
||||
|
||||
if (modeType == 0)
|
||||
MainController.UsingPorts.Add(Global.Settings.RedirectorTCPPort);
|
||||
|
||||
text.Append(")");
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
|
||||
public void OnBandwidthUpdated(long download)
|
||||
{
|
||||
try
|
||||
@@ -198,7 +182,7 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
UsedBandwidthLabel.Text =
|
||||
$"{i18N.Translate("Used",": ")}{Bandwidth.Compute(upload + download)}";
|
||||
$"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(upload + download)}";
|
||||
UploadSpeedLabel.Text = $"↑: {Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
|
||||
DownloadSpeedLabel.Text = $"↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.ServiceProcess;
|
||||
@@ -134,8 +133,8 @@ namespace Netch.Forms
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
MainController = new MainController();
|
||||
MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
_mainController = new MainController();
|
||||
_mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
foreach (var item in Global.Settings.SubscribeLink)
|
||||
@@ -165,8 +164,8 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
// ignored
|
||||
}
|
||||
|
||||
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
|
||||
var result = ShareLink.Parse(response);
|
||||
@@ -203,7 +202,7 @@ namespace Netch.Forms
|
||||
{
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
MainController.Stop();
|
||||
_mainController.Stop();
|
||||
NatTypeStatusLabel.Text = "";
|
||||
}
|
||||
|
||||
@@ -213,7 +212,6 @@ namespace Netch.Forms
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
UpdateStatus(bak_State);
|
||||
StatusLabel.Text = bak_StateText;
|
||||
|
||||
}).ContinueWith(task => { BeginInvoke(new Action(() => { UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = true; })); });
|
||||
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
@@ -232,39 +230,6 @@ namespace Netch.Forms
|
||||
|
||||
#region 选项
|
||||
|
||||
private void RestartServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Restarting service"));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = new ServiceController("netfilter2");
|
||||
if (service.Status == ServiceControllerStatus.Stopped)
|
||||
{
|
||||
service.Start();
|
||||
service.WaitForStatus(ServiceControllerStatus.Running);
|
||||
}
|
||||
else if (service.Status == ServiceControllerStatus.Running)
|
||||
{
|
||||
service.Stop();
|
||||
service.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||
service.Start();
|
||||
service.WaitForStatus(ServiceControllerStatus.Running);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
NFAPI.nf_registerDriver("netfilter2");
|
||||
}
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Service has been restarted"), owner: this);
|
||||
Enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
@@ -281,7 +246,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(i18N.Translate("Error", e.Message));
|
||||
MessageBoxX.Show(e.ToString(),info:false);
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
@@ -380,8 +345,8 @@ namespace Netch.Forms
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
MainController = new MainController();
|
||||
MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
_mainController = new MainController();
|
||||
_mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
|
||||
using var client = new WebClient();
|
||||
|
||||
@@ -403,7 +368,7 @@ namespace Netch.Forms
|
||||
finally
|
||||
{
|
||||
UpdateStatus(State.Waiting);
|
||||
MainController.Stop();
|
||||
_mainController.Stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,19 +12,18 @@ namespace Netch.Forms
|
||||
public partial class Dummy { }
|
||||
partial class MainForm
|
||||
{
|
||||
|
||||
/// init at <see cref="MainForm_Load"/>
|
||||
private int _sizeHeight;
|
||||
private int _controlHeight;
|
||||
private int _profileBoxHeight;
|
||||
|
||||
private int _profileConfigurationHeight;
|
||||
private int _profileGroupboxHeight;
|
||||
private int _configurationGroupBoxHeight;
|
||||
|
||||
private void InitProfile()
|
||||
{
|
||||
// Clear
|
||||
foreach (var button in ProfileButtons)
|
||||
{
|
||||
button.Dispose();
|
||||
}
|
||||
|
||||
ProfileButtons.Clear();
|
||||
ProfileTable.ColumnStyles.Clear();
|
||||
@@ -33,51 +32,51 @@ namespace Netch.Forms
|
||||
var numProfile = Global.Settings.ProfileCount;
|
||||
if (numProfile == 0)
|
||||
{
|
||||
// Hide Profile GroupBox, Change window size
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.Percent;
|
||||
configLayoutPanel.RowStyles[2].Height = 0;
|
||||
ProfileGroupBox.Visible = false;
|
||||
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _controlHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight - (_controlHeight + _profileBoxHeight));
|
||||
|
||||
return;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _profileConfigurationHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight - (_profileConfigurationHeight + _profileGroupboxHeight));
|
||||
}
|
||||
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||||
ProfileGroupBox.Visible = true;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight);
|
||||
|
||||
|
||||
ProfileTable.ColumnCount = numProfile;
|
||||
|
||||
while (Global.Settings.Profiles.Count < numProfile)
|
||||
else
|
||||
{
|
||||
Global.Settings.Profiles.Add(new Profile());
|
||||
}
|
||||
// Load Profiles
|
||||
ProfileTable.ColumnCount = numProfile;
|
||||
|
||||
// buttons
|
||||
for (var i = 0; i < numProfile; ++i)
|
||||
{
|
||||
var b = new Button();
|
||||
ProfileTable.Controls.Add(b, i, 0);
|
||||
b.Location = new Point(i * 100, 0);
|
||||
b.Click += ProfileButton_Click;
|
||||
b.Dock = DockStyle.Fill;
|
||||
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
|
||||
while (Global.Settings.Profiles.Count < numProfile)
|
||||
{
|
||||
Global.Settings.Profiles.Add(new Profile());
|
||||
}
|
||||
|
||||
ProfileButtons.Add(b);
|
||||
}
|
||||
for (var i = 0; i < numProfile; ++i)
|
||||
{
|
||||
var b = new Button();
|
||||
b.Click += ProfileButton_Click;
|
||||
b.Dock = DockStyle.Fill;
|
||||
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
|
||||
|
||||
// equal column
|
||||
for (var i = 1; i <= ProfileTable.RowCount; i++)
|
||||
{
|
||||
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
|
||||
}
|
||||
ProfileTable.Controls.Add(b, i, 0);
|
||||
ProfileButtons.Add(b);
|
||||
}
|
||||
|
||||
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
|
||||
{
|
||||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||||
// equal column
|
||||
for (var i = 1; i <= ProfileTable.RowCount; i++)
|
||||
{
|
||||
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
|
||||
}
|
||||
|
||||
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
|
||||
{
|
||||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||||
}
|
||||
|
||||
if (Size.Height == _sizeHeight) return;
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||||
ProfileGroupBox.Visible = true;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +128,18 @@ namespace Netch.Forms
|
||||
Global.Settings.Profiles[index] = new Profile(selectedServer, selectedMode, name);
|
||||
}
|
||||
|
||||
public List<Button> ProfileButtons = new List<Button>();
|
||||
private void RemoveProfile(int index)
|
||||
{
|
||||
Global.Settings.Profiles[index] = new Profile();
|
||||
}
|
||||
|
||||
|
||||
private List<Button> ProfileButtons = new List<Button>();
|
||||
|
||||
private void ProfileButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var index = ProfileButtons.IndexOf((Button) sender);
|
||||
|
||||
//Utils.Logging.Info(String.Format("Button no.{0} clicked", index));
|
||||
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
@@ -145,7 +148,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else if (ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
else if (ProfileNameText.Text == "")
|
||||
{
|
||||
@@ -156,51 +159,53 @@ namespace Netch.Forms
|
||||
SaveProfile(index);
|
||||
ProfileButtons[index].Text = ProfileNameText.Text;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (Global.Settings.Profiles[index].IsDummy)
|
||||
{
|
||||
if (ProfileButtons[index].Text == i18N.Translate("Error") || ProfileButtons[index].Text == i18N.Translate("None"))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
|
||||
}
|
||||
MessageBoxX.Show(i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProfileNameText.Text = LoadProfile(index);
|
||||
if (ModifierKeys == Keys.Shift)
|
||||
{
|
||||
if (MessageBoxX.Show(i18N.Translate("Remove this Profile?"), confirm: true) != DialogResult.OK) return;
|
||||
RemoveProfile(index);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
MessageBoxX.Show(i18N.Translate("Profile Removed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// start the profile
|
||||
var need2ndStart = true;
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
{
|
||||
need2ndStart = false;
|
||||
}
|
||||
try
|
||||
{
|
||||
ProfileNameText.Text = LoadProfile(index);
|
||||
|
||||
ControlButton.PerformClick();
|
||||
|
||||
if (need2ndStart)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (State != State.Stopped)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
ControlButton.PerformClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
// start the profile
|
||||
ControlFun();
|
||||
if (State == State.Stopping || State == State.Stopped)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info(ee.Message);
|
||||
ProfileButtons[index].Text = i18N.Translate("Error");
|
||||
Thread.Sleep(1200);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
while (State != State.Stopped)
|
||||
{
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
ControlFun();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info(ee.ToString());
|
||||
ProfileButtons[index].Text = i18N.Translate("Error");
|
||||
Thread.Sleep(1200);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Forms
|
||||
{
|
||||
#region Server
|
||||
|
||||
public void TestServer()
|
||||
private static void TestServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -24,6 +24,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ namespace Netch.Forms
|
||||
|
||||
#region Mode
|
||||
|
||||
public void InitMode()
|
||||
private void InitMode()
|
||||
{
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Clear();
|
||||
@@ -139,7 +140,7 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectLastMode()
|
||||
private void SelectLastMode()
|
||||
{
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
|
||||
@@ -182,7 +183,7 @@ namespace Netch.Forms
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Init at MainForm_Load()
|
||||
/// Init at <see cref="MainForm_Load"/>
|
||||
/// </summary>
|
||||
private int _eWidth;
|
||||
|
||||
@@ -205,32 +206,18 @@ namespace Netch.Forms
|
||||
|
||||
switch (cbx.Items[e.Index])
|
||||
{
|
||||
case Models.Server _:
|
||||
case Models.Server item:
|
||||
{
|
||||
var item = cbx.Items[e.Index] as Models.Server;
|
||||
|
||||
// 计算延迟底色
|
||||
SolidBrush brush;
|
||||
if (item.Delay > 200)
|
||||
{
|
||||
// 红色
|
||||
brush = new SolidBrush(Color.Red);
|
||||
}
|
||||
else if (item.Delay > 80)
|
||||
{
|
||||
// 黄色
|
||||
brush = new SolidBrush(Color.Yellow);
|
||||
}
|
||||
else if (item.Delay >= 0)
|
||||
{
|
||||
// 绿色
|
||||
brush = new SolidBrush(Color.FromArgb(50, 255, 56));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 灰色
|
||||
brush = new SolidBrush(Color.Gray);
|
||||
}
|
||||
|
||||
// 绘制延迟底色
|
||||
e.Graphics.FillRectangle(brush, _eWidth * 9, e.Bounds.Y, _eWidth, e.Bounds.Height);
|
||||
@@ -240,10 +227,8 @@ namespace Netch.Forms
|
||||
_eWidth * 9 + _eWidth / 30, e.Bounds.Y);
|
||||
break;
|
||||
}
|
||||
case Models.Mode _:
|
||||
case Models.Mode item:
|
||||
{
|
||||
var item = cbx.Items[e.Index] as Models.Mode;
|
||||
|
||||
// 绘制 模式Box 底色
|
||||
e.Graphics.FillRectangle(new SolidBrush(Color.Gray), _eWidth * 9, e.Bounds.Y, _eWidth,
|
||||
e.Bounds.Height);
|
||||
@@ -257,6 +242,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Netch.Forms
|
||||
/// </summary>
|
||||
public State State { get; private set; } = State.Waiting;
|
||||
|
||||
public void NatTypeStatusText(string text = "",string Country = "")
|
||||
public void NatTypeStatusText(string text = "", string country = "")
|
||||
{
|
||||
if (State != State.Started)
|
||||
{
|
||||
@@ -27,9 +27,9 @@ namespace Netch.Forms
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (Country != "")
|
||||
if (country != "")
|
||||
{
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}[{2}]", i18N.Translate(": "), text, Country);
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}[{2}]", i18N.Translate(": "), text, country);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -43,12 +43,16 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
NatTypeStatusLabel.Text = "NAT" + i18N.Translate(": ") + i18N.Translate("Test failed");
|
||||
NatTypeStatusLabel.Text = $@"NAT{i18N.Translate(": ", "Test failed")}";
|
||||
}
|
||||
|
||||
NatTypeStatusLabel.Visible = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新 NAT指示灯颜色
|
||||
/// </summary>
|
||||
/// <param name="natType"></param>
|
||||
private void UpdateNatTypeLight(STUN_Client.NatType natType)
|
||||
{
|
||||
Color c;
|
||||
@@ -76,19 +80,34 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态栏文本
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
public void StatusText(string text)
|
||||
{
|
||||
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update UI, Status, Status Label
|
||||
/// 更新 UI, 状态栏文本, 状态
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public void UpdateStatus(State state)
|
||||
/// <param name="text"></param>
|
||||
private void UpdateStatus(State state, string text = "")
|
||||
{
|
||||
State = state;
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(state)));
|
||||
StatusText(text == "" ? i18N.Translate(StateExtension.GetStatusString(state)) : text);
|
||||
|
||||
void MenuStripsEnabled(bool enabled)
|
||||
{
|
||||
// 需要禁用的菜单项
|
||||
UninstallServiceToolStripMenuItem.Enabled =
|
||||
updateACLWithProxyToolStripMenuItem.Enabled =
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||||
reinstallTapDriverToolStripMenuItem.Enabled = enabled;
|
||||
}
|
||||
|
||||
// TODO 补充
|
||||
switch (state)
|
||||
{
|
||||
@@ -96,34 +115,33 @@ namespace Netch.Forms
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = true;
|
||||
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Starting:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ServerComboBox.Enabled = false;
|
||||
ModeComboBox.Enabled = false;
|
||||
ConfigurationGroupBox.Enabled = false;
|
||||
|
||||
UninstallServiceToolStripMenuItem.Enabled = false;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = false;
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = false;
|
||||
reinstallTapDriverToolStripMenuItem.Enabled = false;
|
||||
MenuStripsEnabled(false);
|
||||
break;
|
||||
case State.Started:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Stop");
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
//LastDownloadBandwidth = 0;
|
||||
//UploadSpeedLabel.Text = "↑: 0 KB/s";
|
||||
DownloadSpeedLabel.Text = @"↑↓: 0 KB/s";
|
||||
UsedBandwidthLabel.Text = $@"{i18N.Translate("Used", ": ")}0 KB";
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = true;
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ProfileGroupBox.Enabled = false;
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = SettingsButton.Enabled = true;
|
||||
UsedBandwidthLabel.Visible = UploadSpeedLabel.Visible = DownloadSpeedLabel.Visible = false;
|
||||
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = false;
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Stopped:
|
||||
@@ -133,14 +151,10 @@ namespace Netch.Forms
|
||||
LastUploadBandwidth = 0;
|
||||
LastDownloadBandwidth = 0;
|
||||
|
||||
ServerComboBox.Enabled = true;
|
||||
ModeComboBox.Enabled = true;
|
||||
ProfileGroupBox.Enabled = true;
|
||||
ConfigurationGroupBox.Enabled = true;
|
||||
|
||||
UninstallServiceToolStripMenuItem.Enabled = true;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = true;
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = true;
|
||||
reinstallTapDriverToolStripMenuItem.Enabled = true;
|
||||
MenuStripsEnabled(true);
|
||||
break;
|
||||
case State.Terminating:
|
||||
|
||||
@@ -148,7 +162,10 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateStatus()
|
||||
/// <summary>
|
||||
/// 刷新 UI
|
||||
/// </summary>
|
||||
private void UpdateStatus()
|
||||
{
|
||||
UpdateStatus(State);
|
||||
}
|
||||
|
||||
@@ -19,18 +19,7 @@ namespace Netch.Forms
|
||||
/// <summary>
|
||||
/// 主控制器
|
||||
/// </summary>
|
||||
public MainController MainController;
|
||||
|
||||
/// <summary>
|
||||
/// 是否第一次打开
|
||||
/// </summary>
|
||||
public bool IsFirstOpened = true;
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
public static MainForm Instance;
|
||||
|
||||
private MainController _mainController = new MainController();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -40,8 +29,6 @@ namespace Netch.Forms
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
// MenuStrip.Renderer = new Override.ToolStripProfessionalRender();
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void SaveConfigs()
|
||||
@@ -64,6 +51,9 @@ namespace Netch.Forms
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
// 计算 ComboBox绘制 目标宽度
|
||||
_eWidth = ServerComboBox.Width / 10;
|
||||
|
||||
// 加载服务器
|
||||
InitServer();
|
||||
|
||||
@@ -73,18 +63,16 @@ namespace Netch.Forms
|
||||
// 加载翻译
|
||||
InitText();
|
||||
|
||||
//
|
||||
// 隐藏 NatTypeStatusLabel
|
||||
NatTypeStatusText();
|
||||
|
||||
// 加载快速配置
|
||||
_sizeHeight = Size.Height;
|
||||
_controlHeight = ConfigurationGroupBox.Controls[0].Height / 3;
|
||||
_profileBoxHeight = ProfileGroupBox.Height;
|
||||
_configurationGroupBoxHeight = ConfigurationGroupBox.Height;
|
||||
_profileConfigurationHeight = ConfigurationGroupBox.Controls[0].Height / 3; // 因为 AutoSize, 所以得到的是Controls的总高度
|
||||
_profileGroupboxHeight = ProfileGroupBox.Height;
|
||||
// 加载快速配置
|
||||
InitProfile();
|
||||
|
||||
// 为 ComboBox绘制 收集宽度数据
|
||||
_eWidth = ServerComboBox.Width / 10;
|
||||
|
||||
// 自动检测延迟
|
||||
Task.Run(() =>
|
||||
@@ -132,7 +120,7 @@ namespace Netch.Forms
|
||||
WindowState = FormWindowState.Minimized;
|
||||
NotifyIcon.Visible = true;
|
||||
|
||||
if (IsFirstOpened)
|
||||
if (_isFirstCloseWindow)
|
||||
{
|
||||
// 显示提示语
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
@@ -140,7 +128,7 @@ namespace Netch.Forms
|
||||
i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."),
|
||||
ToolTipIcon.Info);
|
||||
|
||||
IsFirstOpened = false;
|
||||
_isFirstCloseWindow = false;
|
||||
}
|
||||
|
||||
Hide();
|
||||
@@ -155,8 +143,6 @@ namespace Netch.Forms
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
//防止模式选择框变成蓝色:D
|
||||
ModeComboBox.Select(0, 0);
|
||||
ControlFun();
|
||||
}
|
||||
|
||||
@@ -184,7 +170,7 @@ namespace Netch.Forms
|
||||
InitProfile();
|
||||
}
|
||||
|
||||
public void InitText()
|
||||
private void InitText()
|
||||
{
|
||||
ServerToolStripMenuItem.Text = i18N.Translate("Server");
|
||||
ImportServersFromClipboardToolStripMenuItem.Text = i18N.Translate("Import Servers From Clipboard");
|
||||
@@ -207,16 +193,16 @@ namespace Netch.Forms
|
||||
reinstallTapDriverToolStripMenuItem.Text = i18N.Translate("Reinstall TUN/TAP driver");
|
||||
OpenDirectoryToolStripMenuItem.Text = i18N.Translate("Open Directory");
|
||||
AboutToolStripButton.Text = i18N.Translate("About");
|
||||
VersionLabel.Text = i18N.Translate("xxx");
|
||||
// VersionLabel.Text = i18N.Translate("xxx");
|
||||
exitToolStripMenuItem.Text = i18N.Translate("Exit");
|
||||
RelyToolStripMenuItem.Text = i18N.Translate("Unable to start? Click me to download");
|
||||
ConfigurationGroupBox.Text = i18N.Translate("Configuration");
|
||||
ProfileLabel.Text = i18N.Translate("Profile");
|
||||
ModeLabel.Text = i18N.Translate("Mode");
|
||||
ServerLabel.Text = i18N.Translate("Server");
|
||||
UsedBandwidthLabel.Text = i18N.Translate("Used: 0 KB");
|
||||
DownloadSpeedLabel.Text = i18N.Translate("↓: 0 KB/s");
|
||||
UploadSpeedLabel.Text = i18N.Translate("↑: 0 KB/s");
|
||||
// UsedBandwidthLabel.Text = i18N.Translate("Used: 0 KB");
|
||||
// DownloadSpeedLabel.Text = i18N.Translate("↓: 0 KB/s");
|
||||
// UploadSpeedLabel.Text = i18N.Translate("↑: 0 KB/s");
|
||||
NotifyIcon.Text = i18N.Translate("Netch");
|
||||
ShowMainFormToolStripButton.Text = i18N.Translate("Show");
|
||||
ExitToolStripButton.Text = i18N.Translate("Exit");
|
||||
@@ -230,6 +216,51 @@ namespace Netch.Forms
|
||||
VersionLabel.Text = UpdateChecker.Version;
|
||||
}
|
||||
|
||||
private void Exit(bool forceExit = false)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
|
||||
// 已启动
|
||||
if (State != State.Waiting && State != State.Stopped)
|
||||
{
|
||||
if (Global.Settings.StopWhenExited || forceExit)
|
||||
{
|
||||
UpdateStatus(State.Stopping);
|
||||
ControlFun();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 未开启自动停止
|
||||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||||
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
NotifyIcon.Visible = true; // 托盘图标隐藏
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NotifyIcon.Visible = false;
|
||||
Hide();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
break;
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
SaveConfigs();
|
||||
UpdateStatus(State.Terminating);
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
});
|
||||
}
|
||||
|
||||
#region MISC
|
||||
|
||||
/// <summary>
|
||||
@@ -303,7 +334,6 @@ namespace Netch.Forms
|
||||
Enabled = true;
|
||||
StatusText(i18N.Translate("Test done"));
|
||||
Refresh();
|
||||
Configuration.Save();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,7 +356,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +381,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +398,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -414,49 +445,6 @@ namespace Netch.Forms
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void Exit(bool forceExit = false)
|
||||
{
|
||||
// 已启动
|
||||
if (State != State.Waiting && State != State.Stopped)
|
||||
{
|
||||
if (forceExit)
|
||||
ControlFun();
|
||||
else
|
||||
{
|
||||
if (!Global.Settings.StopWhenExited)
|
||||
{
|
||||
// 未开启自动停止
|
||||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||||
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
NotifyIcon.Visible = true; // 托盘图标隐藏
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotifyIcon.Visible = false;
|
||||
Hide();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
break;
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
SaveConfigs();
|
||||
UpdateStatus(State.Terminating);
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
});
|
||||
}
|
||||
|
||||
private void ExitToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Exit();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using TaskScheduler;
|
||||
|
||||
@@ -33,6 +34,7 @@ namespace Netch.Forms
|
||||
dns += ip;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
TUNTAPDNSTextBox.Text = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
@@ -120,6 +122,7 @@ namespace Netch.Forms
|
||||
dns += ip;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
TUNTAPDNSTextBox.Text = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
@@ -183,7 +186,10 @@ namespace Netch.Forms
|
||||
folder.GetTask("Netch Startup");
|
||||
taskIsExists = true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (RunAtStartup.Checked)
|
||||
{
|
||||
@@ -196,7 +202,7 @@ namespace Netch.Forms
|
||||
task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
|
||||
|
||||
task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
|
||||
var action = (IExecAction)task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
var action = (IExecAction) task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
action.Path = Application.ExecutablePath;
|
||||
|
||||
|
||||
@@ -204,7 +210,7 @@ namespace Netch.Forms
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int)_TASK_CREATION.TASK_CREATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -212,77 +218,15 @@ namespace Netch.Forms
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var Socks5Port = int.Parse(Socks5PortTextBox.Text);
|
||||
|
||||
if (Socks5Port > 0 && Socks5Port < 65536)
|
||||
{
|
||||
Global.Settings.Socks5LocalPort = Socks5Port;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
Socks5PortTextBox.Text = Global.Settings.Socks5LocalPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
// 端口检查
|
||||
if (!CheckPortText("Socks5", ref Socks5PortTextBox, ref Global.Settings.Socks5LocalPort))
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var HTTPPort = int.Parse(HTTPPortTextBox.Text);
|
||||
|
||||
if (HTTPPort > 0 && HTTPPort < 65536)
|
||||
{
|
||||
Global.Settings.HTTPLocalPort = HTTPPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
HTTPPortTextBox.Text = Global.Settings.HTTPLocalPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
if (!CheckPortText("HTTP", ref HTTPPortTextBox, ref Global.Settings.HTTPLocalPort))
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var RedirectorPort = int.Parse(RedirectorTextBox.Text);
|
||||
|
||||
if (RedirectorPort > 0 && RedirectorPort < 65536)
|
||||
{
|
||||
Global.Settings.RedirectorTCPPort = RedirectorPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
RedirectorTextBox.Text = Global.Settings.RedirectorTCPPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
if (!CheckPortText("RedirectorTCP", ref RedirectorTextBox, ref Global.Settings.RedirectorTCPPort, PortType.TCP))
|
||||
return;
|
||||
}
|
||||
|
||||
if (AllowDevicesCheckBox.Checked)
|
||||
{
|
||||
Global.Settings.LocalAddress = "0.0.0.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.LocalAddress = "127.0.0.1";
|
||||
}
|
||||
Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1";
|
||||
|
||||
try
|
||||
{
|
||||
@@ -310,12 +254,14 @@ namespace Netch.Forms
|
||||
DNS += ip;
|
||||
DNS += ',';
|
||||
}
|
||||
|
||||
DNS = DNS.Trim();
|
||||
TUNTAPDNSTextBox.Text = DNS.Substring(0, DNS.Length - 1);
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ProfileCount = int.Parse(ProfileCount_TextBox.Text);
|
||||
@@ -336,6 +282,7 @@ namespace Netch.Forms
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var STUN_Server = STUN_ServerTextBox.Text;
|
||||
@@ -359,6 +306,7 @@ namespace Netch.Forms
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Global.Settings.StartedTcping = EnableStartedTcping_CheckBox.Checked;
|
||||
@@ -402,5 +350,43 @@ namespace Netch.Forms
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="portName"></param>
|
||||
/// <param name="portTextBox"></param>
|
||||
/// <param name="originPort"></param>
|
||||
/// <param name="portType"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FormatException"></exception>
|
||||
private bool CheckPortText(string portName, ref TextBox portTextBox, ref int originPort, PortType portType = PortType.Both)
|
||||
{
|
||||
// 端口检查
|
||||
try
|
||||
{
|
||||
var port = int.Parse(portTextBox.Text);
|
||||
|
||||
if (port <= 0 || port >= 65536)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
if (PortHelper.PortInUse(port, portType))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", portName));
|
||||
return false;
|
||||
}
|
||||
|
||||
originPort = port;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace Netch.Forms
|
||||
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Successfully saved"));
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
}
|
||||
});
|
||||
if (saveFlag)
|
||||
@@ -200,7 +200,7 @@ namespace Netch.Forms
|
||||
{
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Successfully saved"));
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Netch
|
||||
public static readonly string NetchDir = (AppDomain.CurrentDomain.BaseDirectory).TrimEnd();
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
public static Forms.MainForm MainForm;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -57,6 +58,8 @@ namespace Netch
|
||||
|
||||
// 记录当前系统语言
|
||||
Logging.Info($"当前语言:{Global.Settings.Language}");
|
||||
Logging.Info($"版本:{UpdateChecker.Owner}/{UpdateChecker.Repo} {UpdateChecker.Version}");
|
||||
Logging.Info($"主程序创建日期:{File.GetCreationTime(Global.NetchDir + "\\Netch.exe"):yyyy-M-d HH:mm}");
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
@@ -68,20 +71,6 @@ namespace Netch
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
var OS = Environment.Is64BitOperatingSystem ? "x64" : "x86";
|
||||
var PROC = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
|
||||
// 如果系统位数与程序位数不一致
|
||||
if (OS != PROC)
|
||||
{
|
||||
|
||||
// 弹出提示
|
||||
MessageBoxX.Show($"{i18N.Translate("Netch is not compatible with your system.")}\n{i18N.Translate("Current arch of Netch:")} {PROC}\n{i18N.Translate("Current arch of system:")} {OS}");
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
// 绑定错误捕获
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
Application.ThreadException += Application_OnException;
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Web" />
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
"Information": "信息",
|
||||
"Error": "错误",
|
||||
|
||||
"If this is your first time using this software,\n please check http://netch.org to install supports first,\n or the program may report errors.": "如果你是第一次使用本软件,\n请务必前往http://netch.org安装程序所需依赖,\n否则程序将无法正常运行!",
|
||||
"Netch is already running": "Netch 已经在运行中",
|
||||
"Netch is not compatible with your system.": "Netch 和你的系统不兼容",
|
||||
"Current arch of Netch:": "当前 Netch 架构:",
|
||||
"Current arch of system:": "当前系统架构:",
|
||||
|
||||
"Start": "启动",
|
||||
"Stop": "停止",
|
||||
@@ -17,7 +15,6 @@
|
||||
"Stopping": "正在停止中",
|
||||
"Stopped": "已停止",
|
||||
"Starting ": "正在启动 ",
|
||||
"v2ray": "V2Ray",
|
||||
"Starting Tap": "正在启动 TUN/TAP",
|
||||
"Starting NatTester": "正在启动 NAT 测试",
|
||||
"Starting LocalDns service": "正在启动本地 DNS 服务",
|
||||
@@ -40,7 +37,6 @@
|
||||
"Add [ShadowsocksR] Server": "添加 [ShadowsocksR] 服务器",
|
||||
"Add [VMess] Server": "添加 [VMess] 服务器",
|
||||
"Add [Trojan] Server": "添加 [Trojan] 服务器",
|
||||
"VMess is currently not supported. For more information, please see our Github releases\n\nPress OK will redirect": "当前不支持 VMess 服务器。需要更多信息请查看我们的 Github 发布页\n\n点击 OK 将会跳转",
|
||||
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口。",
|
||||
"New version available": "发现新版本",
|
||||
"Mode": "模式",
|
||||
@@ -78,9 +74,6 @@
|
||||
"Update servers error from {0}": "从 {0} 更新服务器失败",
|
||||
|
||||
"Options": "选项",
|
||||
"Restart Service": "重启服务",
|
||||
"Restarting service": "正在重启服务中",
|
||||
"Service has been restarted": "服务已重启",
|
||||
"Uninstall Service": "卸载服务",
|
||||
"Uninstalling Service": "正在卸载服务中",
|
||||
"Service has been uninstalled": "服务已卸载",
|
||||
@@ -106,9 +99,11 @@
|
||||
|
||||
"Please press Stop button first": "请先点击停止按钮",
|
||||
"Please select a server first": "请先选择一个服务器",
|
||||
"Please select an mode first": "请先选择一个模式",
|
||||
"Please select a mode first": "请先选择一个模式",
|
||||
"Please enter a profile name first": "请先为该配置设置一个名称",
|
||||
"No saved profile here. Save a profile first by Ctrl+Click on the button": "当前按钮下没有保存配置,请先使用 CTRL + 左键 点击该按钮保存一个配置",
|
||||
"Remove this Profile?": "确认删除此配置?",
|
||||
"Profile Removed!": "配置已删除!",
|
||||
|
||||
"Used": "已使用",
|
||||
"Status": "状态",
|
||||
@@ -142,7 +137,6 @@
|
||||
"Remark can not be empty": "备注不可为空",
|
||||
"Link can not be empty": "链接不可为空",
|
||||
"Link must start with http:// or https://": "链接必须以 http:// 或 https:// 开头",
|
||||
"Successfully saved": "保存成功",
|
||||
"Please fill in alterID": "请填写额外ID",
|
||||
|
||||
"Settings": "设置",
|
||||
@@ -169,7 +163,6 @@
|
||||
"Detection interval value illegal. Try again.": "检测间隔值非法。请重试。",
|
||||
"TUN/TAP driver is not detected. Is it installed now?": "未检测到 TUN/TAP 驱动,是否现在安装?",
|
||||
"Failed to set the system proxy, it may be caused by the lack of dependent programs. Do you want to jump to Netch's official website to download dependent programs?": "设置系统代理失败,可能是缺少依赖导致,是否跳转 Netch 官网下载依赖程序?",
|
||||
"Experimental function": "实验性功能",
|
||||
"Delay test after start": "启动后延迟测试",
|
||||
"Enable": "启用",
|
||||
"Detection interval(sec)": "检测间隔(秒)",
|
||||
@@ -177,7 +170,6 @@
|
||||
"STUN Server Port": "STUN 服务器端口",
|
||||
"Custom ACL": "自定义 ACL 规则",
|
||||
"Language": "语言",
|
||||
"Saved.": "保存成功",
|
||||
|
||||
"Profile": "配置名",
|
||||
"Profiles": "配置",
|
||||
@@ -187,6 +179,8 @@
|
||||
"Exit": "退出",
|
||||
"Unable to start? Click me to download": "无法启动?点我下载依赖",
|
||||
|
||||
"The {0} port is in use.": "{0} 端口已被占用",
|
||||
|
||||
"Bypass LAN": "[网页代理] 绕过局域网",
|
||||
"Bypass LAN (Non System Proxy)": "[网页代理] 绕过局域网(不设置系统代理)",
|
||||
"Bypass LAN (TUN/TAP)": "[TUN/TAP] 绕过局域网",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
@@ -48,27 +49,27 @@ namespace Netch.Utils
|
||||
//int sent = 0;
|
||||
|
||||
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
|
||||
var processList = new List<int>();
|
||||
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.AkaName == "HTTP")
|
||||
var instances = new List<Process>();
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.Name == "HTTP")
|
||||
{
|
||||
processList.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance.Id);
|
||||
instances.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance);
|
||||
}
|
||||
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL)
|
||||
{
|
||||
processList.Add(Process.GetCurrentProcess().Id);
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
}
|
||||
else if (mainController.pEncryptedProxyController != null)
|
||||
{
|
||||
// mainController.pServerClientController.Instance
|
||||
processList.Add(mainController.pEncryptedProxyController.Instance.Id);
|
||||
instances.Add(mainController.pEncryptedProxyController.Instance);
|
||||
}
|
||||
else if (mainController.pModeController != null)
|
||||
{
|
||||
processList.Add(mainController.pModeController.Instance.Id);
|
||||
instances.Add(mainController.pModeController.Instance);
|
||||
}
|
||||
|
||||
Logging.Info("启动流量统计 PID:" + string.Join(",", processList.ToArray()));
|
||||
var processList = instances.Select(instance => instance.Id).ToList();
|
||||
|
||||
Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})"+instance.ProcessName).ToArray()));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -101,18 +102,18 @@ namespace Netch.Utils
|
||||
}
|
||||
});
|
||||
|
||||
if ((Convert.ToInt32(MainForm.Instance.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
|
||||
if ((Convert.ToInt32(Global.MainForm.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
|
||||
{
|
||||
MainForm.Instance.OnBandwidthUpdated(0);
|
||||
Global.MainForm.OnBandwidthUpdated(0);
|
||||
received = 0;
|
||||
}
|
||||
|
||||
while (MainForm.Instance.State != State.Stopped)
|
||||
while (Global.MainForm.State != State.Stopped)
|
||||
{
|
||||
Task.Delay(1000).Wait();
|
||||
lock (counterLock)
|
||||
{
|
||||
MainForm.Instance.OnBandwidthUpdated(Convert.ToInt64(received));
|
||||
Global.MainForm.OnBandwidthUpdated(Convert.ToInt64(received));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ namespace Netch.Utils
|
||||
else
|
||||
{
|
||||
// 弹出提示
|
||||
MessageBoxX.Show("如果你是第一次使用本软件\n请务必前往http://netch.org 安装程序所需依赖,\n否则程序将无法正常运行!", i18N.Translate("注意!"));
|
||||
i18N.Load("System");
|
||||
MessageBoxX.Show(i18N.Translate("If this is your first time using this software,\n please check http://netch.org to install supports first,\n or the program may report errors."));
|
||||
|
||||
// 创建 data 文件夹并保存默认设置
|
||||
Save();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -45,5 +48,51 @@ namespace Netch.Utils
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置DNS
|
||||
/// </summary>
|
||||
/// <param name="dns"></param>
|
||||
public static void SetDNS(string[] dns)
|
||||
{
|
||||
ManagementClass wmi = new ManagementClass("Win32_NetworkAdapterConfiguration");
|
||||
ManagementObjectCollection moc = wmi.GetInstances();
|
||||
ManagementBaseObject inPar = null;
|
||||
ManagementBaseObject outPar = null;
|
||||
foreach (ManagementObject mo in moc)
|
||||
{
|
||||
//如果没有启用IP设置的网络设备则跳过,如果是虚拟机网卡也跳过
|
||||
if (!(bool)mo["IPEnabled"] ||
|
||||
mo["Description"].ToString().Contains("Virtual") ||
|
||||
mo["Description"].ToString().Contains("VMware") ||
|
||||
mo["Description"].ToString().Contains("TAP"))
|
||||
continue;
|
||||
|
||||
//设置DNS地址
|
||||
if (dns != null)
|
||||
{
|
||||
inPar = mo.GetMethodParameters("SetDNSServerSearchOrder");
|
||||
inPar["DNSServerSearchOrder"] = dns;
|
||||
outPar = mo.InvokeMethod("SetDNSServerSearchOrder", inPar, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 从网卡获取ip设置信息
|
||||
/// </summary>
|
||||
public static string[] getSystemDns()
|
||||
{
|
||||
var systemDns = new[] { "223.5.5.5", "1.1.1.1" };
|
||||
foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
|
||||
if (!network.Description.Contains("Virtual") &&
|
||||
!network.Description.Contains("VMware") &&
|
||||
!network.Description.Contains("TAP") &&
|
||||
network.OperationalStatus == OperationalStatus.Up &&
|
||||
network.GetIPProperties()?.GatewayAddresses.Count != 0)
|
||||
{
|
||||
systemDns = network.GetIPProperties().DnsAddresses.Select(dns => dns.ToString()).ToArray();
|
||||
}
|
||||
|
||||
return systemDns;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NetFwTypeLib;
|
||||
|
||||
namespace Netch.Utils
|
||||
@@ -37,7 +38,14 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void RemoveFwRules()
|
||||
{
|
||||
RemoveFwRules(NetchAutoRule);
|
||||
try
|
||||
{
|
||||
RemoveFwRules(NetchAutoRule);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("添加防火墙规则错误\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,16 +53,23 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void AddNetchFwRules()
|
||||
{
|
||||
if (GetFwRulePath(Netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(Netch) >= ProgramPath.Length) return;
|
||||
RemoveNetchFwRules();
|
||||
foreach (var p in ProgramPath)
|
||||
try
|
||||
{
|
||||
var path = Path.GetFullPath(p);
|
||||
if (File.Exists(path))
|
||||
if (GetFwRulePath(Netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(Netch) >= ProgramPath.Length) return;
|
||||
RemoveNetchFwRules();
|
||||
foreach (var p in ProgramPath)
|
||||
{
|
||||
AddFwRule("Netch", path);
|
||||
var path = Path.GetFullPath(p);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
AddFwRule("Netch", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("添加防火墙规则错误(如已关闭防火墙则可无视此错误)\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +77,15 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
private static void RemoveNetchFwRules()
|
||||
{
|
||||
RemoveFwRules(Netch);
|
||||
try
|
||||
{
|
||||
RemoveFwRules(Netch);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("清除防火墙规则错误\n" + e);
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#region 封装
|
||||
@@ -76,7 +99,7 @@ namespace Netch.Utils
|
||||
rule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
|
||||
// ApplicationName 大小不敏感
|
||||
rule.ApplicationName = exeFullPath;
|
||||
// rule.Description = "Used to block all internet access.";
|
||||
// rule.Description = "";
|
||||
rule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
|
||||
rule.Enabled = true;
|
||||
rule.InterfaceTypes = "All";
|
||||
@@ -87,17 +110,10 @@ namespace Netch.Utils
|
||||
|
||||
private static void RemoveFwRules(string ruleName)
|
||||
{
|
||||
try
|
||||
var c = GetFwRulesNumber(ruleName);
|
||||
foreach (var _ in new bool[c])
|
||||
{
|
||||
var c = GetFwRulesNumber(ruleName);
|
||||
foreach (var _ in new bool[c])
|
||||
{
|
||||
FwPolicy.Rules.Remove(ruleName);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Info("Netch 自带程序添加防火墙出错(如已关闭防火墙则可无视此错误)\n" + e);
|
||||
FwPolicy.Rules.Remove(ruleName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +127,8 @@ namespace Netch.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
var rule = (INetFwRule2)FwPolicy.Rules.Item(ruleName);
|
||||
return rule.ApplicationName;
|
||||
var rule = (INetFwRule2) FwPolicy.Rules.Item(ruleName);
|
||||
return rule.ApplicationName;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -122,14 +138,7 @@ namespace Netch.Utils
|
||||
|
||||
private static int GetFwRulesNumber(string ruleName)
|
||||
{
|
||||
// https://stackoverflow.com/a/53601691
|
||||
var i = 0;
|
||||
foreach (INetFwRule2 rule in FwPolicy.Rules)
|
||||
{
|
||||
if (rule.Name == ruleName)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
return FwPolicy.Rules.Cast<INetFwRule2>().Count(rule => rule.Name == ruleName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class Logging
|
||||
{
|
||||
private const string LogFile = "logging\\application.log";
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
@@ -12,16 +13,25 @@ namespace Netch.Utils
|
||||
/// <param name="text">内容</param>
|
||||
public static void Info(string text)
|
||||
{
|
||||
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][INFO] {text}{Global.EOF}");
|
||||
File.AppendAllText(LogFile, $@"[{DateTime.Now}][INFO] {text}{Global.EOF}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
/// <param name="text">内容</param>
|
||||
public static void Warning(string text)
|
||||
{
|
||||
File.AppendAllText(LogFile, $@"[{DateTime.Now}][WARNING] {text}{Global.EOF}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
/// <param name="text">内容</param>
|
||||
public static void Error(string text)
|
||||
{
|
||||
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][ERROR] {text}{Global.EOF}");
|
||||
File.AppendAllText(LogFile, $@"[{DateTime.Now}][ERROR] {text}{Global.EOF}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Utils
|
||||
{
|
||||
return MessageBox.Show(
|
||||
owner: owner,
|
||||
text: i18N.Translate(text: text),
|
||||
text: text,
|
||||
caption: i18N.Translate(string.IsNullOrWhiteSpace(title) ? (info ? "Information" : "Error") : title),
|
||||
buttons: confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK,
|
||||
icon: info ? MessageBoxIcon.Information : MessageBoxIcon.Exclamation);
|
||||
|
||||
34
Netch/Utils/PortHelper.cs
Normal file
34
Netch/Utils/PortHelper.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Netch.Controllers;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class PortHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 指定类型的端口是否已经被使用了
|
||||
/// </summary>
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="type">检查端口类型</param>
|
||||
/// <returns>是否被占用</returns>
|
||||
public static bool PortInUse(int port, PortType type = PortType.Both)
|
||||
{
|
||||
var netInfo = IPGlobalProperties.GetIPGlobalProperties();
|
||||
var tcpResult = type != PortType.UDP && netInfo.GetActiveTcpListeners().Any(ipEndPoint => !MainController.UsingPorts.Contains(ipEndPoint.Port) && ipEndPoint.Port == port);
|
||||
var udpResult = type != PortType.TCP && netInfo.GetActiveUdpListeners().Any(ipEndPoint => !MainController.UsingPorts.Contains(ipEndPoint.Port) && ipEndPoint.Port == port);
|
||||
|
||||
return tcpResult || udpResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查端口类型
|
||||
/// </summary>
|
||||
public enum PortType
|
||||
{
|
||||
TCP,
|
||||
UDP,
|
||||
Both
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace Netch.Utils
|
||||
|
||||
if (DnsResult != null)
|
||||
{
|
||||
Country = databaseReader.Country(Hostname).Country.IsoCode;
|
||||
Country = databaseReader.Country(DnsResult).Country.IsoCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Netch.Utils
|
||||
public static void Load(string langCode)
|
||||
{
|
||||
LangCode = langCode;
|
||||
|
||||
|
||||
var text = "";
|
||||
if (langCode.Equals("System"))
|
||||
{
|
||||
@@ -34,8 +34,6 @@ namespace Netch.Utils
|
||||
langCode = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (langCode == "zh-CN")
|
||||
{
|
||||
// 尝试加载内置中文语言
|
||||
@@ -69,18 +67,30 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
/// <param name="text">需要翻译的文本</param>
|
||||
/// <returns>翻译完毕的文本</returns>
|
||||
public static string Translate(string text)
|
||||
{
|
||||
return Data.Contains(text) ? Data[text].ToString() : text;
|
||||
}
|
||||
public static string Translate(params string[] text)
|
||||
public static string Translate(params object[] text)
|
||||
{
|
||||
var a = new StringBuilder();
|
||||
foreach (var t in text)
|
||||
a.Append(Data.Contains(t) ? Data[t].ToString() : t);
|
||||
if (t is string)
|
||||
a.Append(Data.Contains(t) ? Data[t].ToString() : t);
|
||||
else
|
||||
a.Append(t);
|
||||
return a.ToString();
|
||||
}
|
||||
|
||||
|
||||
public static string TranslateFormat(string format, params object[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] is string)
|
||||
{
|
||||
args[i] = Translate((string) args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format(Translate(format), args);
|
||||
}
|
||||
|
||||
public static List<string> GetTranslateList()
|
||||
{
|
||||
var translateFile = new List<string> {"System", "zh-CN", "en-US"};
|
||||
|
||||
2
binaries
2
binaries
Submodule binaries updated: 841b75af2f...2adb17a398
Reference in New Issue
Block a user