Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e1d70c29e | ||
|
|
a47395cc6c | ||
|
|
e3bc370019 | ||
|
|
825b7bd6a6 | ||
|
|
2705f5c160 | ||
|
|
fd8185b83e | ||
|
|
b3866d1d90 | ||
|
|
bb2fbfaafa | ||
|
|
efa1b2ed54 | ||
|
|
60aa010096 | ||
|
|
68880f0c9b | ||
|
|
7310126a39 | ||
|
|
389ac32094 | ||
|
|
2676e97ca0 | ||
|
|
1c231ee897 | ||
|
|
549988b2de | ||
|
|
18963fa14b | ||
|
|
81da544108 | ||
|
|
d88d1b2e0c | ||
|
|
a4081b1a7f | ||
|
|
f76a1f1a8e | ||
|
|
d60716cf4f |
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Telegram Channel
|
||||
url: https://t.me/Netch
|
||||
about: Telegram Channel
|
||||
- name: Telegram Group
|
||||
url: https://t.me/Netch_Discuss_Group
|
||||
about: Telegram Group
|
||||
@@ -12,7 +12,7 @@ using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class MainController
|
||||
public static class MainController
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前使用的端口
|
||||
@@ -20,14 +20,14 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public static readonly List<int> UsingPorts = new List<int>();
|
||||
|
||||
public EncryptedProxy pEncryptedProxyController { get; private set; }
|
||||
public static EncryptedProxy EncryptedProxyController { get; private set; }
|
||||
|
||||
public ModeController pModeController { get; private set; }
|
||||
public static ModeController ModeController { get; private set; }
|
||||
|
||||
private Server _savedServer;
|
||||
private Mode _savedMode;
|
||||
private static Server _savedServer;
|
||||
private static Mode _savedMode;
|
||||
|
||||
public string PortInfo
|
||||
public static string PortInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -48,7 +48,11 @@ namespace Netch.Controllers
|
||||
|
||||
if (_savedMode.Type == 3 || _savedMode.Type == 5)
|
||||
// 有HTTP
|
||||
text.Append($" | HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
{
|
||||
if (_savedServer.Type != "Socks5")
|
||||
text.Append(" | ");
|
||||
text.Append($"HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
}
|
||||
|
||||
return $" ({text})";
|
||||
}
|
||||
@@ -57,12 +61,13 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// NTT 控制器
|
||||
/// </summary>
|
||||
public NTTController pNTTController = new NTTController();
|
||||
public static readonly NTTController NTTController = new NTTController();
|
||||
|
||||
private static string _localAddress;
|
||||
private static int _redirectorTCPPort;
|
||||
private static int _httpPort;
|
||||
private static int _socks5Port;
|
||||
|
||||
private string _localAddress;
|
||||
private int _redirectorTCPPort;
|
||||
private int _httpPort;
|
||||
private int _socks5Port;
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
|
||||
public static extern uint FlushDNSResolverCache();
|
||||
@@ -73,16 +78,16 @@ namespace Netch.Controllers
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public async Task<bool> Start(Server server, Mode mode)
|
||||
public static async Task<bool> Start(Server server, Mode mode)
|
||||
{
|
||||
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
|
||||
|
||||
#region Record Settings
|
||||
|
||||
_httpPort = Global.Settings.HTTPLocalPort;
|
||||
_socks5Port = Global.Settings.Socks5LocalPort;
|
||||
_socks5Port = server.Type != "Socks5" ? Global.Settings.Socks5LocalPort : server.Port;
|
||||
_redirectorTCPPort = Global.Settings.RedirectorTCPPort;
|
||||
_localAddress = Global.Settings.LocalAddress;
|
||||
_localAddress = server.Type != "Socks5" ? Global.Settings.LocalAddress : "127.0.0.1";
|
||||
_savedServer = server;
|
||||
_savedMode = mode;
|
||||
|
||||
@@ -98,45 +103,48 @@ namespace Netch.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
pEncryptedProxyController = server.Type switch
|
||||
EncryptedProxyController = server.Type switch
|
||||
{
|
||||
"SS" => new SSController(),
|
||||
"SSR" => new SSRController(),
|
||||
"VMess" => new VMessController(),
|
||||
"Trojan" => new TrojanController(),
|
||||
_ => pEncryptedProxyController
|
||||
_ => EncryptedProxyController
|
||||
};
|
||||
|
||||
KillProcessByName(pEncryptedProxyController.MainFile);
|
||||
KillProcessByName(EncryptedProxyController.MainFile);
|
||||
|
||||
// 检查端口是否被占用
|
||||
var isPortNotAvailable = false;
|
||||
#region 检查端口是否被占用
|
||||
|
||||
var portNotAvailable = false;
|
||||
if (_savedServer.Type != "Socks5")
|
||||
{
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
|
||||
}
|
||||
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
case 0:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPortNotAvailable)
|
||||
if (portNotAvailable)
|
||||
{
|
||||
Logging.Error("主控制器启动失败: 端口被占用");
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name));
|
||||
#endregion
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", EncryptedProxyController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pEncryptedProxyController.Start(server, mode));
|
||||
result = await Task.Run(() => EncryptedProxyController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -148,32 +156,32 @@ namespace Netch.Controllers
|
||||
if (result)
|
||||
{
|
||||
// 加密代理成功启动
|
||||
UsingPorts.Add(pEncryptedProxyController?.Socks5LocalPort ?? server.Port); // 记录Socks5使用端口
|
||||
UsingPorts.Add(_socks5Port);
|
||||
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0: // 进程代理模式
|
||||
pModeController = new NFController();
|
||||
ModeController = new NFController();
|
||||
break;
|
||||
case 1: // TUN/TAP 黑名单代理模式
|
||||
case 2: // TUN/TAP 白名单代理模式
|
||||
pModeController = new TUNTAPController();
|
||||
ModeController = new TUNTAPController();
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
pModeController = new HTTPController();
|
||||
ModeController = new HTTPController();
|
||||
break;
|
||||
case 4: // Socks5 代理模式,不需要启动额外的Server
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pModeController != null)
|
||||
if (ModeController != null)
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", ModeController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pModeController.Start(server, mode));
|
||||
result = await Task.Run(() => ModeController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -205,15 +213,7 @@ namespace Netch.Controllers
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
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) Global.MainForm.NatTypeStatusText(natType, country);
|
||||
});
|
||||
NatTest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -235,17 +235,48 @@ namespace Netch.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool NttTested;
|
||||
|
||||
/// <summary>
|
||||
/// 测试 NAT
|
||||
/// </summary>
|
||||
public static void NatTest()
|
||||
{
|
||||
NttTested = false;
|
||||
Task.Run(() =>
|
||||
{
|
||||
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
// Thread.Sleep(1000);
|
||||
var (nttResult, natType, localEnd, publicEnd) = NTTController.Start();
|
||||
|
||||
if (nttResult)
|
||||
{
|
||||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||||
Global.MainForm.NatTypeStatusText(natType, country);
|
||||
}
|
||||
else
|
||||
Global.MainForm.NatTypeStatusText(natType);
|
||||
|
||||
NttTested = true;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public async Task Stop()
|
||||
public static async Task Stop()
|
||||
{
|
||||
_httpPort = _socks5Port = _redirectorTCPPort = 0;
|
||||
_localAddress = null;
|
||||
_savedMode = null;
|
||||
_savedServer = null;
|
||||
UsingPorts.Clear();
|
||||
|
||||
var tasks = new Task[]
|
||||
{
|
||||
Task.Run(() => pEncryptedProxyController?.Stop()),
|
||||
Task.Run(() => UsingPorts.Clear()),
|
||||
Task.Run(() => pModeController?.Stop()),
|
||||
Task.Run(() => pNTTController.Stop())
|
||||
Task.Run(() => EncryptedProxyController?.Stop()),
|
||||
Task.Run(() => ModeController?.Stop()),
|
||||
Task.Run(() => NTTController.Stop())
|
||||
};
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
@@ -77,12 +78,6 @@ namespace Netch.Controllers
|
||||
Logging.Info("绕行 → 服务器 IP");
|
||||
_directIPs.AddRange(_serverAddresses.Where(address => !IPAddress.IsLoopback(address)).Select(address => IPNetwork.Parse(address.ToString(), 32)));
|
||||
|
||||
if (_savedMode.BypassChina)
|
||||
{
|
||||
Logging.Info("绕行 → 中国 IP");
|
||||
_directIPs.AddRange(Encoding.UTF8.GetString(Resources.CNIP).Split('\n').Select(IPNetwork.Parse));
|
||||
}
|
||||
|
||||
Logging.Info("绕行 → 局域网 IP");
|
||||
_directIPs.AddRange(_bypassLanIPs.Select(IPNetwork.Parse));
|
||||
|
||||
@@ -146,6 +141,19 @@ namespace Netch.Controllers
|
||||
}
|
||||
);
|
||||
|
||||
if (_savedMode.BypassChina)
|
||||
{
|
||||
Logging.Info("绕行 → 中国 IP");
|
||||
|
||||
foreach (var str in File.ReadAllLines("bin\\china_ip_list", Encoding.UTF8))
|
||||
{
|
||||
if (IPNetwork.TryParse(str, out var entry))
|
||||
{
|
||||
_directIPs.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Info("绕行 → 规则 IP");
|
||||
_directIPs.AddRange(_savedMode.Rule.Select(IPNetwork.Parse));
|
||||
|
||||
@@ -204,14 +212,7 @@ namespace Netch.Controllers
|
||||
//if (Global.Settings.TUNTAP.UseCustomDNS || server.Type.Equals("VMess"))
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = Global.Settings.TUNTAP.DNS.Aggregate(string.Empty, (current, value) => current + (value + ',')).Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
else
|
||||
@@ -231,8 +232,7 @@ namespace Netch.Controllers
|
||||
argument.Append($"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{adapterName}\"");
|
||||
|
||||
State = State.Starting;
|
||||
if (!StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime)) return false;
|
||||
return true;
|
||||
return StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,9 +358,12 @@ namespace Netch.Controllers
|
||||
Delete
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType, int metric = 0)
|
||||
private static void RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType, int metric = 0)
|
||||
{
|
||||
return ipNetworks.All(address => RouteAction(action, address, routeType, metric));
|
||||
foreach (var address in ipNetworks)
|
||||
{
|
||||
RouteAction(action, address, routeType, metric);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, string address, byte cidr, RouteType routeType, int metric = 0)
|
||||
@@ -386,12 +389,19 @@ namespace Netch.Controllers
|
||||
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
|
||||
}
|
||||
|
||||
return action switch
|
||||
var result = action switch
|
||||
{
|
||||
Action.Create => NativeMethods.CreateRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
Action.Delete => NativeMethods.DeleteRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(action), action, null)
|
||||
};
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Logging.Warning($"{action} Route on {routeType} Adapter failed: {ipNetwork} metric {metric}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class NTTController : Controller
|
||||
{
|
||||
private string _Other_address;
|
||||
private string _Binding_test;
|
||||
private string _Local_address;
|
||||
private string _Mapped_address;
|
||||
private string _Nat_mapping_behavior;
|
||||
private string _Nat_filtering_behavior;
|
||||
private string _lastResult;
|
||||
private string _localEnd;
|
||||
private string _publicEnd;
|
||||
private string _natType;
|
||||
private bool _nttResult;
|
||||
|
||||
public NTTController()
|
||||
{
|
||||
@@ -27,6 +25,9 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public (bool, string, string, string) Start()
|
||||
{
|
||||
_nttResult = false;
|
||||
_natType = _localEnd = _publicEnd = null;
|
||||
|
||||
try
|
||||
{
|
||||
InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
|
||||
@@ -36,17 +37,7 @@ namespace Netch.Controllers
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
Instance.WaitForExit();
|
||||
|
||||
/* var result = _lastResult.Split('\n');
|
||||
var natType = result[0];
|
||||
var localEnd = result[1];
|
||||
var publicEnd = result[2];*/
|
||||
|
||||
var natType = _lastResult;
|
||||
var localEnd = _Local_address;
|
||||
var publicEnd = _Mapped_address;
|
||||
|
||||
return (true, natType, localEnd, publicEnd);
|
||||
return (_nttResult, _natType, _localEnd, _publicEnd);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -67,40 +58,32 @@ namespace Netch.Controllers
|
||||
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Data)) return;
|
||||
var str = e.Data.Split(':');
|
||||
Logging.Info($"[NTT] {e.Data}");
|
||||
|
||||
var str = e.Data.Split(':').Select(s => s.Trim()).ToArray();
|
||||
if (str.Length < 2)
|
||||
return;
|
||||
var key = str[0];
|
||||
var value = str[1].Trim();
|
||||
var value = str[1];
|
||||
switch (key)
|
||||
{
|
||||
case "Other address is":
|
||||
_Other_address = value;
|
||||
Logging.Info($"[NTT] Other address is {value}");
|
||||
break;
|
||||
case "Binding test":
|
||||
_Binding_test = value;
|
||||
Logging.Info($"[NTT] Binding test {value}");
|
||||
case "Nat mapping behavior":
|
||||
case "Nat filtering behavior":
|
||||
break;
|
||||
case "Local address":
|
||||
_Local_address = value;
|
||||
Logging.Info($"[NTT] Local address {value}");
|
||||
_localEnd = value;
|
||||
break;
|
||||
case "Mapped address":
|
||||
_Mapped_address = value;
|
||||
Logging.Info($"[NTT] Mapped address {value}");
|
||||
break;
|
||||
case "Nat mapping behavior":
|
||||
_Nat_mapping_behavior = value;
|
||||
Logging.Info($"[NTT] Nat mapping behavior {value}");
|
||||
break;
|
||||
case "Nat filtering behavior":
|
||||
_Nat_filtering_behavior = value;
|
||||
Logging.Info($"[NTT] Nat filtering behavior {value}");
|
||||
_publicEnd = value;
|
||||
break;
|
||||
case "result":
|
||||
_lastResult = value;
|
||||
Logging.Info($"[NTT] result {value}");
|
||||
_natType = value;
|
||||
_nttResult = true;
|
||||
break;
|
||||
default:
|
||||
_natType = str.Last();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Controllers
|
||||
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019 - 2020";
|
||||
public const string Version = @"1.5.0";
|
||||
public const string Version = @"1.5.1";
|
||||
|
||||
public string LatestVersionNumber;
|
||||
public string LatestVersionUrl;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -42,10 +44,10 @@ namespace Netch.Forms
|
||||
var server = ServerComboBox.SelectedItem as Models.Server;
|
||||
var mode = ModeComboBox.SelectedItem as Models.Mode;
|
||||
|
||||
if (await _mainController.Start(server, mode))
|
||||
if (await MainController.Start(server, mode))
|
||||
{
|
||||
State = State.Started;
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode, ref _mainController); });
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode); });
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
{
|
||||
@@ -87,17 +89,17 @@ namespace Netch.Forms
|
||||
{
|
||||
// 停止
|
||||
State = State.Stopping;
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
State = State.Stopped;
|
||||
_ = Task.Run(TestServer);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long download)
|
||||
public void OnBandwidthUpdated(ulong download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long>(OnBandwidthUpdated), download);
|
||||
BeginInvoke(new Action<ulong>(OnBandwidthUpdated), download);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,44 +119,14 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long upload, long download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long, long>(OnBandwidthUpdated), upload, download);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (upload < 1 || download < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UsedBandwidthLabel.Text =
|
||||
$"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(upload + download)}";
|
||||
UploadSpeedLabel.Text = $"↑: {Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
|
||||
DownloadSpeedLabel.Text = $"↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
|
||||
|
||||
LastUploadBandwidth = upload;
|
||||
LastDownloadBandwidth = download;
|
||||
Refresh();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上一次上传的流量
|
||||
/// </summary>
|
||||
public long LastUploadBandwidth;
|
||||
public ulong LastUploadBandwidth;
|
||||
|
||||
/// <summary>
|
||||
/// 上一次下载的流量
|
||||
/// </summary>
|
||||
public long LastDownloadBandwidth;
|
||||
public ulong LastDownloadBandwidth;
|
||||
}
|
||||
}
|
||||
2
Netch/Forms/MainForm.Designer.cs
generated
@@ -605,6 +605,7 @@ namespace Netch.Forms
|
||||
this.NatTypeStatusLabel.Size = new System.Drawing.Size(36, 17);
|
||||
this.NatTypeStatusLabel.Text = "NAT:";
|
||||
this.NatTypeStatusLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
|
||||
this.NatTypeStatusLabel.Click += new System.EventHandler(this.NatTypeStatusLabel_Click);
|
||||
//
|
||||
// NatTypeStatusLightLabel
|
||||
//
|
||||
@@ -616,6 +617,7 @@ namespace Netch.Forms
|
||||
this.NatTypeStatusLightLabel.Size = new System.Drawing.Size(18, 21);
|
||||
this.NatTypeStatusLightLabel.Text = "⬤";
|
||||
this.NatTypeStatusLightLabel.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||
this.NatTypeStatusLightLabel.Click += new System.EventHandler(this.NatTypeStatusLabel_Click);
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Netch.Forms
|
||||
|
||||
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Form form = ((ToolStripMenuItem) sender).Name switch
|
||||
Form form = ((ToolStripMenuItem)sender).Name switch
|
||||
{
|
||||
"AddSocks5ServerToolStripMenuItem" => new Socks5(),
|
||||
"AddShadowsocksServerToolStripMenuItem" => new Shadowsocks(),
|
||||
@@ -113,6 +113,12 @@ namespace Netch.Forms
|
||||
|
||||
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
UpdateServersFromSubscribe();
|
||||
}
|
||||
|
||||
public async void UpdateServersFromSubscribe()
|
||||
{
|
||||
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
|
||||
@@ -144,7 +150,7 @@ namespace Netch.Forms
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
var serverLock = new object();
|
||||
@@ -209,7 +215,7 @@ namespace Netch.Forms
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
}
|
||||
|
||||
DisableItems(true);
|
||||
@@ -297,7 +303,7 @@ namespace Netch.Forms
|
||||
Type = 5
|
||||
};
|
||||
State = State.Starting;
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
var req = WebUtil.CreateRequest(Global.Settings.ACL);
|
||||
@@ -316,7 +322,7 @@ namespace Netch.Forms
|
||||
{
|
||||
if (useProxy)
|
||||
{
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -68,11 +69,11 @@ namespace Netch.Forms
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Stop");
|
||||
|
||||
StatusTextAppend(_mainController.PortInfo);
|
||||
StatusTextAppend(MainController.PortInfo);
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = true;
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = Bandwidth.NetTrafficAvailable;
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
@@ -126,6 +127,7 @@ namespace Netch.Forms
|
||||
{
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}", i18N.Translate(": "), text);
|
||||
}
|
||||
|
||||
if (int.TryParse(text, out int natType))
|
||||
{
|
||||
if (natType > 0 && natType < 5)
|
||||
@@ -134,6 +136,10 @@ namespace Netch.Forms
|
||||
UpdateNatTypeLight(natType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -16,10 +16,6 @@ namespace Netch.Forms
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 主控制器
|
||||
/// </summary>
|
||||
private MainController _mainController = new MainController();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -87,6 +83,16 @@ namespace Netch.Forms
|
||||
CheckUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
// 检查订阅更新
|
||||
if (Global.Settings.UpdateSubscribeatWhenOpened)
|
||||
{
|
||||
UpdateServersFromSubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCalled(object sender, OnlyInstance.Commands e)
|
||||
@@ -441,5 +447,13 @@ namespace Netch.Forms
|
||||
if (!_comboBoxInitialized) return;
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
private void NatTypeStatusLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_state == State.Started && MainController.NttTested)
|
||||
{
|
||||
MainController.NatTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
{
|
||||
public partial class Process : Form
|
||||
{
|
||||
//用于判断当前窗口是否为编辑模式
|
||||
public readonly bool IsEditing;
|
||||
/// <summary>
|
||||
/// 被编辑的模式
|
||||
/// </summary>
|
||||
private readonly Models.Mode _mode;
|
||||
|
||||
//被编辑的模式
|
||||
public readonly Models.Mode EditingMode;
|
||||
|
||||
public bool IsEdited { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// 是否被编辑过
|
||||
/// </summary>
|
||||
public bool Edited { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编辑模式
|
||||
@@ -24,19 +28,25 @@ namespace Netch.Forms.Mode
|
||||
/// <param name="mode">模式</param>
|
||||
public Process(Models.Mode mode)
|
||||
{
|
||||
if (mode.Type != 0)
|
||||
{
|
||||
throw new Exception("请传入进程模式");
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
|
||||
EditingMode = mode;
|
||||
Text = "Edit Process Mode";
|
||||
//循环填充已有规则
|
||||
mode.Rule.ForEach(i => RuleListBox.Items.Add(i));
|
||||
this._mode = mode;
|
||||
RuleListBox.Items.AddRange(mode.Rule.ToArray());
|
||||
|
||||
#region 禁用文件名更改
|
||||
|
||||
IsEditing = true;
|
||||
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
UseCustomFilenameBox.Enabled = false;
|
||||
FilenameTextBox.Enabled =
|
||||
UseCustomFilenameBox.Enabled = false;
|
||||
|
||||
#endregion
|
||||
|
||||
FilenameTextBox.Text = mode.FileName;
|
||||
RemarkTextBox.Text = mode.Remark;
|
||||
@@ -47,11 +57,7 @@ namespace Netch.Forms.Mode
|
||||
InitializeComponent();
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
|
||||
EditingMode = null;
|
||||
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
IsEditing = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,7 +97,7 @@ namespace Netch.Forms.Mode
|
||||
if (FileChildInfo.Name.EndsWith(".exe") && !RuleListBox.Items.Contains(FileChildInfo.Name))
|
||||
{
|
||||
RuleListBox.Items.Add(FileChildInfo.Name);
|
||||
IsEdited = true;
|
||||
Edited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +139,7 @@ namespace Netch.Forms.Mode
|
||||
{
|
||||
if (RuleListBox.SelectedIndex == -1) return;
|
||||
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
|
||||
IsEdited = true;
|
||||
Edited = true;
|
||||
}
|
||||
|
||||
private async void AddButton_Click(object sender, EventArgs e)
|
||||
@@ -153,7 +159,7 @@ namespace Netch.Forms.Mode
|
||||
RuleListBox.Items.Add(process);
|
||||
}
|
||||
|
||||
IsEdited = true;
|
||||
Edited = true;
|
||||
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
}
|
||||
@@ -166,115 +172,76 @@ namespace Netch.Forms.Mode
|
||||
|
||||
private void ScanButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var dialog = new FolderBrowserDialog();
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
var dialog = new CommonOpenFileDialog
|
||||
{
|
||||
ScanDirectory(dialog.SelectedPath);
|
||||
IsFolderPicker = true,
|
||||
Multiselect = false,
|
||||
Title = i18N.Translate("Select a folder"),
|
||||
AddToMostRecentlyUsedList = false,
|
||||
EnsurePathExists = true,
|
||||
NavigateToShortcut = true
|
||||
};
|
||||
if (dialog.ShowDialog(Win32Native.GetForegroundWindow()) == CommonFileDialogResult.Ok)
|
||||
{
|
||||
ScanDirectory(dialog.FileName);
|
||||
MessageBoxX.Show(i18N.Translate("Scan completed"));
|
||||
}
|
||||
}
|
||||
|
||||
public void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (IsEditing)
|
||||
if (RuleListBox.Items.Count == 0)
|
||||
{
|
||||
// 编辑模式
|
||||
if (RuleListBox.Items.Count != 0)
|
||||
{
|
||||
EditingMode.BypassChina = false;
|
||||
EditingMode.FileName = FilenameTextBox.Text;
|
||||
EditingMode.Type = 0;
|
||||
EditingMode.Remark = RemarkTextBox.Text;
|
||||
EditingMode.Rule.Clear();
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
return;
|
||||
}
|
||||
|
||||
var text = $"# {RemarkTextBox.Text}, 0\r\n";
|
||||
foreach (var item in RuleListBox.Items)
|
||||
{
|
||||
var process = item as string;
|
||||
EditingMode.Rule.Add(process);
|
||||
text += process + "\r\n";
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
|
||||
return;
|
||||
}
|
||||
|
||||
text = text.Substring(0, text.Length - 2);
|
||||
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists("mode"))
|
||||
{
|
||||
Directory.CreateDirectory("mode");
|
||||
}
|
||||
if (_mode != null)
|
||||
{
|
||||
_mode.Remark = RemarkTextBox.Text;
|
||||
_mode.Rule.Clear();
|
||||
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
|
||||
File.WriteAllText(Path.Combine("mode", FilenameTextBox.Text) + ".txt", text);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
|
||||
IsEdited = false;
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
}
|
||||
Modes.WriteFile(_mode);
|
||||
Edited = false;
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Configuration.Save();
|
||||
if (!string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
var fullName = Modes.GetFullPath(FilenameTextBox.Text + ".txt");
|
||||
if (File.Exists(fullName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
var modeFilename = Path.Combine("mode", FilenameTextBox.Text);
|
||||
// 如果文件已存在,返回
|
||||
if (File.Exists(modeFilename + ".txt"))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleListBox.Items.Count != 0)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
BypassChina = false,
|
||||
FileName = FilenameTextBox.Text,
|
||||
Type = 0,
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
|
||||
var text = $"# {RemarkTextBox.Text}, 0\r\n";
|
||||
foreach (var item in RuleListBox.Items)
|
||||
{
|
||||
var process = item as string;
|
||||
mode.Rule.Add(process);
|
||||
text += process + "\r\n";
|
||||
}
|
||||
|
||||
text = text.Substring(0, text.Length - 2);
|
||||
|
||||
if (!Directory.Exists("mode"))
|
||||
{
|
||||
Directory.CreateDirectory("mode");
|
||||
}
|
||||
|
||||
File.WriteAllText(modeFilename + ".txt", text);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
|
||||
Modes.Add(mode);
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
}
|
||||
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
|
||||
}
|
||||
BypassChina = false,
|
||||
FileName = FilenameTextBox.Text,
|
||||
Type = 0,
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
|
||||
Modes.WriteFile(mode);
|
||||
Modes.Add(mode);
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
|
||||
@@ -297,7 +264,7 @@ namespace Netch.Forms.Mode
|
||||
|
||||
private void UseCustomFilenameBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
FilenameTextBox.Enabled = FilenameLabel.Enabled = ((CheckBox) sender).Checked;
|
||||
FilenameTextBox.Enabled = UseCustomFilenameBox.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Netch/Forms/SettingForm.Designer.cs
generated
@@ -63,7 +63,7 @@
|
||||
this.TcpingAtStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUNServerLabel = new System.Windows.Forms.Label();
|
||||
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUN_ServerComboBox = new System.Windows.Forms.SearchComboBox();
|
||||
this.STUN_ServerComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.MinimizeWhenStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ProfileCountLabel = new System.Windows.Forms.Label();
|
||||
this.ProfileCountTextBox = new System.Windows.Forms.TextBox();
|
||||
@@ -72,6 +72,7 @@
|
||||
this.StartWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.StopWhenExitedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ExitWhenClosedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.PortGroupBox.SuspendLayout();
|
||||
this.TUNTAPGroupBox.SuspendLayout();
|
||||
this.BehaviorGroupBox.SuspendLayout();
|
||||
@@ -276,7 +277,7 @@
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.ControlButton.Location = new System.Drawing.Point(804, 356);
|
||||
this.ControlButton.Name = "ControlButton";
|
||||
this.ControlButton.Size = new System.Drawing.Size(75, 23);
|
||||
@@ -287,7 +288,7 @@
|
||||
//
|
||||
// GlobalBypassIPsButton
|
||||
//
|
||||
this.GlobalBypassIPsButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.GlobalBypassIPsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.GlobalBypassIPsButton.Location = new System.Drawing.Point(12, 356);
|
||||
this.GlobalBypassIPsButton.Name = "GlobalBypassIPsButton";
|
||||
this.GlobalBypassIPsButton.Size = new System.Drawing.Size(128, 23);
|
||||
@@ -298,6 +299,7 @@
|
||||
//
|
||||
// BehaviorGroupBox
|
||||
//
|
||||
this.BehaviorGroupBox.Controls.Add(this.UpdateSubscribeatWhenOpenedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageComboBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ModifySystemDNSCheckBox);
|
||||
@@ -517,6 +519,17 @@
|
||||
this.ExitWhenClosedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.ExitWhenClosedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// UpdateSubscribeatWhenOpenedCheckBox
|
||||
//
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.AutoSize = true;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Location = new System.Drawing.Point(206, 129);
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Name = "UpdateSubscribeatWhenOpenedCheckBox";
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Size = new System.Drawing.Size(224, 21);
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.TabIndex = 24;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Text = "Update subscribeat when opened";
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// SettingForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
@@ -527,9 +540,9 @@
|
||||
this.Controls.Add(this.GlobalBypassIPsButton);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
this.Controls.Add(this.TUNTAPGroupBox);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "SettingForm";
|
||||
@@ -575,7 +588,7 @@
|
||||
private System.Windows.Forms.CheckBox MinimizeWhenStartedCheckBox;
|
||||
private System.Windows.Forms.CheckBox RunAtStartupCheckBox;
|
||||
private System.Windows.Forms.Label STUNServerLabel;
|
||||
private System.Windows.Forms.SearchComboBox STUN_ServerComboBox;
|
||||
private System.Windows.Forms.ComboBox STUN_ServerComboBox;
|
||||
private System.Windows.Forms.CheckBox ProxyDNSCheckBox;
|
||||
private System.Windows.Forms.TextBox DetectionIntervalTextBox;
|
||||
private System.Windows.Forms.CheckBox TcpingAtStartedCheckBox;
|
||||
@@ -590,5 +603,6 @@
|
||||
private System.Windows.Forms.ComboBox LanguageComboBox;
|
||||
private System.Windows.Forms.CheckBox ModifySystemDNSCheckBox;
|
||||
private System.Windows.Forms.CheckBox CheckBetaUpdateCheckBox;
|
||||
private System.Windows.Forms.CheckBox UpdateSubscribeatWhenOpenedCheckBox;
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,7 @@ namespace Netch.Forms
|
||||
BootShadowsocksFromDLLCheckBox.Checked = Global.Settings.BootShadowsocksFromDLL;
|
||||
ModifySystemDNSCheckBox.Checked = Global.Settings.ModifySystemDNS;
|
||||
CheckBetaUpdateCheckBox.Checked = Global.Settings.CheckBetaUpdate;
|
||||
UpdateSubscribeatWhenOpenedCheckBox.Checked = Global.Settings.UpdateSubscribeatWhenOpened;
|
||||
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
TcpingAtStartedCheckBox.Checked = Global.Settings.StartedTcping;
|
||||
@@ -142,6 +143,7 @@ namespace Netch.Forms
|
||||
StartWhenOpenedCheckBox.Text = i18N.Translate(StartWhenOpenedCheckBox.Text);
|
||||
MinimizeWhenStartedCheckBox.Text = i18N.Translate(MinimizeWhenStartedCheckBox.Text);
|
||||
RunAtStartupCheckBox.Text = i18N.Translate(RunAtStartupCheckBox.Text);
|
||||
UpdateSubscribeatWhenOpenedCheckBox.Text = i18N.Translate(UpdateSubscribeatWhenOpenedCheckBox.Text);
|
||||
CheckUpdateWhenOpenedCheckBox.Text = i18N.Translate(CheckUpdateWhenOpenedCheckBox.Text);
|
||||
ProfileCountLabel.Text = i18N.Translate(ProfileCountLabel.Text);
|
||||
TcpingAtStartedCheckBox.Text = i18N.Translate(TcpingAtStartedCheckBox.Text);
|
||||
@@ -186,6 +188,7 @@ namespace Netch.Forms
|
||||
Global.Settings.StartWhenOpened = StartWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckUpdateWhenOpened = CheckUpdateWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckBetaUpdate = CheckBetaUpdateCheckBox.Checked;
|
||||
Global.Settings.UpdateSubscribeatWhenOpened = UpdateSubscribeatWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.MinimizeWhenStarted = MinimizeWhenStartedCheckBox.Checked;
|
||||
Global.Settings.RunAtStartup = RunAtStartupCheckBox.Checked;
|
||||
Global.Settings.BootShadowsocksFromDLL = BootShadowsocksFromDLLCheckBox.Checked;
|
||||
|
||||
76
Netch/Forms/SubscribeForm.Designer.cs
generated
@@ -32,6 +32,7 @@
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SubscribeForm));
|
||||
this.AddSubscriptionBox = new System.Windows.Forms.GroupBox();
|
||||
this.UserAgentTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ClearButton = new System.Windows.Forms.Button();
|
||||
this.AddButton = new System.Windows.Forms.Button();
|
||||
this.UserAgentLabel = new System.Windows.Forms.Label();
|
||||
this.LinkTextBox = new System.Windows.Forms.TextBox();
|
||||
@@ -46,9 +47,12 @@
|
||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UseSelectedServerCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ClearButton = new System.Windows.Forms.Button();
|
||||
this.MainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ControlsPanel = new System.Windows.Forms.Panel();
|
||||
this.AddSubscriptionBox.SuspendLayout();
|
||||
this.pContextMenuStrip.SuspendLayout();
|
||||
this.MainTableLayoutPanel.SuspendLayout();
|
||||
this.ControlsPanel.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// AddSubscriptionBox
|
||||
@@ -61,9 +65,10 @@
|
||||
this.AddSubscriptionBox.Controls.Add(this.LinkLabel);
|
||||
this.AddSubscriptionBox.Controls.Add(this.RemarkTextBox);
|
||||
this.AddSubscriptionBox.Controls.Add(this.RemarkLabel);
|
||||
this.AddSubscriptionBox.Location = new System.Drawing.Point(12, 226);
|
||||
this.AddSubscriptionBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.AddSubscriptionBox.Location = new System.Drawing.Point(8, 214);
|
||||
this.AddSubscriptionBox.Name = "AddSubscriptionBox";
|
||||
this.AddSubscriptionBox.Size = new System.Drawing.Size(660, 135);
|
||||
this.AddSubscriptionBox.Size = new System.Drawing.Size(668, 141);
|
||||
this.AddSubscriptionBox.TabIndex = 1;
|
||||
this.AddSubscriptionBox.TabStop = false;
|
||||
//
|
||||
@@ -74,6 +79,16 @@
|
||||
this.UserAgentTextBox.Size = new System.Drawing.Size(545, 23);
|
||||
this.UserAgentTextBox.TabIndex = 6;
|
||||
//
|
||||
// ClearButton
|
||||
//
|
||||
this.ClearButton.Location = new System.Drawing.Point(477, 103);
|
||||
this.ClearButton.Name = "ClearButton";
|
||||
this.ClearButton.Size = new System.Drawing.Size(58, 26);
|
||||
this.ClearButton.TabIndex = 7;
|
||||
this.ClearButton.Text = "Clear";
|
||||
this.ClearButton.UseVisualStyleBackColor = true;
|
||||
this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click);
|
||||
//
|
||||
// AddButton
|
||||
//
|
||||
this.AddButton.Location = new System.Drawing.Point(541, 103);
|
||||
@@ -130,11 +145,12 @@
|
||||
//
|
||||
this.SubscribeLinkListView.AllowColumnReorder = true;
|
||||
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {this.RemarkColumnHeader, this.LinkColumnHeader, this.UserAgentHeader});
|
||||
this.SubscribeLinkListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.SubscribeLinkListView.FullRowSelect = true;
|
||||
this.SubscribeLinkListView.HideSelection = false;
|
||||
this.SubscribeLinkListView.Location = new System.Drawing.Point(12, 12);
|
||||
this.SubscribeLinkListView.Location = new System.Drawing.Point(8, 8);
|
||||
this.SubscribeLinkListView.Name = "SubscribeLinkListView";
|
||||
this.SubscribeLinkListView.Size = new System.Drawing.Size(660, 208);
|
||||
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 200);
|
||||
this.SubscribeLinkListView.TabIndex = 0;
|
||||
this.SubscribeLinkListView.UseCompatibleStateImageBehavior = false;
|
||||
this.SubscribeLinkListView.View = System.Windows.Forms.View.Details;
|
||||
@@ -179,31 +195,47 @@
|
||||
// UseSelectedServerCheckBox
|
||||
//
|
||||
this.UseSelectedServerCheckBox.AutoSize = true;
|
||||
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(12, 367);
|
||||
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(3, 4);
|
||||
this.UseSelectedServerCheckBox.Name = "UseSelectedServerCheckBox";
|
||||
this.UseSelectedServerCheckBox.Size = new System.Drawing.Size(285, 21);
|
||||
this.UseSelectedServerCheckBox.TabIndex = 9;
|
||||
this.UseSelectedServerCheckBox.Text = "Use Selected Server To Update Subscription";
|
||||
this.UseSelectedServerCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ClearButton
|
||||
// MainTableLayoutPanel
|
||||
//
|
||||
this.ClearButton.Location = new System.Drawing.Point(477, 103);
|
||||
this.ClearButton.Name = "ClearButton";
|
||||
this.ClearButton.Size = new System.Drawing.Size(58, 26);
|
||||
this.ClearButton.TabIndex = 7;
|
||||
this.ClearButton.Text = "Clear";
|
||||
this.ClearButton.UseVisualStyleBackColor = true;
|
||||
this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click);
|
||||
this.MainTableLayoutPanel.ColumnCount = 1;
|
||||
this.MainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.MainTableLayoutPanel.Controls.Add(this.SubscribeLinkListView, 0, 0);
|
||||
this.MainTableLayoutPanel.Controls.Add(this.AddSubscriptionBox, 0, 1);
|
||||
this.MainTableLayoutPanel.Controls.Add(this.ControlsPanel, 0, 2);
|
||||
this.MainTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.MainTableLayoutPanel.Location = new System.Drawing.Point(0, 0);
|
||||
this.MainTableLayoutPanel.Name = "MainTableLayoutPanel";
|
||||
this.MainTableLayoutPanel.Padding = new System.Windows.Forms.Padding(5);
|
||||
this.MainTableLayoutPanel.RowCount = 3;
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 58.35777F));
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 41.64223F));
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
|
||||
this.MainTableLayoutPanel.Size = new System.Drawing.Size(684, 391);
|
||||
this.MainTableLayoutPanel.TabIndex = 11;
|
||||
//
|
||||
// ControlsPanel
|
||||
//
|
||||
this.ControlsPanel.Controls.Add(this.UseSelectedServerCheckBox);
|
||||
this.ControlsPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ControlsPanel.Location = new System.Drawing.Point(5, 358);
|
||||
this.ControlsPanel.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.ControlsPanel.Name = "ControlsPanel";
|
||||
this.ControlsPanel.Size = new System.Drawing.Size(674, 28);
|
||||
this.ControlsPanel.TabIndex = 2;
|
||||
//
|
||||
// SubscribeForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(684, 391);
|
||||
this.Controls.Add(this.UseSelectedServerCheckBox);
|
||||
this.Controls.Add(this.SubscribeLinkListView);
|
||||
this.Controls.Add(this.AddSubscriptionBox);
|
||||
this.Controls.Add(this.MainTableLayoutPanel);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
@@ -217,13 +249,15 @@
|
||||
this.AddSubscriptionBox.ResumeLayout(false);
|
||||
this.AddSubscriptionBox.PerformLayout();
|
||||
this.pContextMenuStrip.ResumeLayout(false);
|
||||
this.MainTableLayoutPanel.ResumeLayout(false);
|
||||
this.ControlsPanel.ResumeLayout(false);
|
||||
this.ControlsPanel.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
|
||||
private System.Windows.Forms.Panel ControlsPanel;
|
||||
private System.Windows.Forms.TableLayoutPanel MainTableLayoutPanel;
|
||||
private System.Windows.Forms.Button ClearButton;
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.GroupBox AddSubscriptionBox;
|
||||
private System.Windows.Forms.Label RemarkLabel;
|
||||
private System.Windows.Forms.TextBox LinkTextBox;
|
||||
@@ -240,5 +274,7 @@
|
||||
private System.Windows.Forms.TextBox UserAgentTextBox;
|
||||
private System.Windows.Forms.ColumnHeader UserAgentHeader;
|
||||
private System.Windows.Forms.CheckBox UseSelectedServerCheckBox;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Netch.Models
|
||||
public string Remark;
|
||||
|
||||
/// <summary>
|
||||
/// 文件相对路径
|
||||
/// 文件相对路径(必须是存在的文件)
|
||||
/// </summary>
|
||||
public string RelativePath;
|
||||
|
||||
@@ -85,50 +85,6 @@ namespace Netch.Models
|
||||
return fileString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入模式文件
|
||||
/// </summary>
|
||||
public void ToFile(string Dir)
|
||||
{
|
||||
if (!System.IO.Directory.Exists(Dir))
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(Dir);
|
||||
}
|
||||
|
||||
var NewPath = System.IO.Path.Combine(Dir, FileName);
|
||||
if (System.IO.File.Exists(NewPath + ".txt"))
|
||||
{
|
||||
// 重命名该模式文件名
|
||||
NewPath += "_";
|
||||
|
||||
while (System.IO.File.Exists(NewPath + ".txt"))
|
||||
{
|
||||
// 循环重命名该模式文件名,直至不重名
|
||||
NewPath += "_";
|
||||
}
|
||||
}
|
||||
|
||||
FileName = System.IO.Path.GetFileName(NewPath);
|
||||
|
||||
// 加上文件名后缀
|
||||
NewPath += ".txt";
|
||||
|
||||
// 写入到模式文件里
|
||||
System.IO.File.WriteAllText(NewPath, ToFileString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除模式文件
|
||||
/// </summary>
|
||||
public void DeleteFile()
|
||||
{
|
||||
var fullPath = Path.Combine(Modes.ModeDirectory, RelativePath);
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public string TypeToString()
|
||||
{
|
||||
return Type switch
|
||||
|
||||
@@ -94,6 +94,11 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public bool CheckBetaUpdate = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否打开软件时更新订阅
|
||||
/// </summary>
|
||||
public bool UpdateSubscribeatWhenOpened = false;
|
||||
|
||||
/// <summary>
|
||||
/// 修改系统 DNS
|
||||
/// </summary>
|
||||
|
||||
@@ -23,6 +23,32 @@ namespace Netch
|
||||
// 设置当前目录
|
||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] {"mode", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
{
|
||||
if (!Directory.Exists(item))
|
||||
{
|
||||
Directory.CreateDirectory(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
{
|
||||
OnlyInstance.Send(OnlyInstance.Commands.Show);
|
||||
Logging.Info("唤起单实例");
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
{
|
||||
@@ -39,42 +65,10 @@ namespace Netch
|
||||
}
|
||||
}
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] {"mode", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
{
|
||||
// 检查是否已经存在
|
||||
if (!Directory.Exists(item))
|
||||
{
|
||||
// 创建目录
|
||||
Directory.CreateDirectory(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}");
|
||||
});
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
{
|
||||
OnlyInstance.Send(OnlyInstance.Commands.Show);
|
||||
Logging.Info("唤起单实例");
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
Task.Run(OnlyInstance.Server);
|
||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}"); });
|
||||
Logging.Info("启动单实例");
|
||||
Task.Run(OnlyInstance.Server);
|
||||
|
||||
// 绑定错误捕获
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
|
||||
@@ -57,10 +57,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILMerge" Version="3.0.41" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.211" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.58" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -107,6 +111,6 @@
|
||||
</ItemGroup>
|
||||
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="@ECHO OFF
RD /S /Q $(TargetDir)bin >NUL 2>&1
RD /S /Q $(TargetDir)i18n >NUL 2>&1
RD /S /Q $(TargetDir)mode >NUL 2>&1

XCOPY /s /Y $(SolutionDir)binaries $(TargetDir)bin\ >NUL
XCOPY /s /Y $(SolutionDir)translations\i18n $(TargetDir)i18n\ >NUL
XCOPY /s /Y $(SolutionDir)modes\mode $(TargetDir)mode\ >NUL

DEL /f $(TargetDir)*.config >NUL 2>&1
DEL /f $(TargetDir)*.pdb >NUL 2>&1
RD /s /Q $(TargetDir)x86 >NUL 2>&1" />
|
||||
<Exec Command="set Configuration=$(Configuration)
set ILMergeConsolePath=$(ILMergeConsolePath)
set TargetDir=$(TargetDir)
set SolutionDir=$(SolutionDir)
$(ProjectDir)PostBuild.bat" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
40
Netch/PostBuild.bat
Normal file
@@ -0,0 +1,40 @@
|
||||
if %Configuration%==Release (
|
||||
:: Merge dlls
|
||||
%ILMergeConsolePath% %TargetDir%Netch.exe ^
|
||||
/out:%TargetDir%NetchMerged.exe ^
|
||||
%TargetDir%Dia2Lib.dll ^
|
||||
%TargetDir%Interop.NetFwTypeLib.dll ^
|
||||
%TargetDir%Interop.TaskScheduler.dll ^
|
||||
%TargetDir%MaxMind.Db.dll ^
|
||||
%TargetDir%MaxMind.GeoIP2.dll ^
|
||||
%TargetDir%Microsoft.Diagnostics.FastSerialization.dll ^
|
||||
%TargetDir%Microsoft.Diagnostics.Tracing.TraceEvent.dll ^
|
||||
%TargetDir%Microsoft.WindowsAPICodePack.dll ^
|
||||
%TargetDir%Microsoft.WindowsAPICodePack.Shell.dll ^
|
||||
%TargetDir%NetchLib.dll ^
|
||||
%TargetDir%Newtonsoft.Json.dll ^
|
||||
%TargetDir%OSExtensions.dll ^
|
||||
%TargetDir%System.Buffers.dll ^
|
||||
%TargetDir%System.Collections.Immutable.dll ^
|
||||
%TargetDir%System.Memory.dll ^
|
||||
%TargetDir%System.Net.IPNetwork.dll ^
|
||||
%TargetDir%System.Numerics.Vectors.dll ^
|
||||
%TargetDir%System.Reflection.Metadata.dll ^
|
||||
%TargetDir%System.Runtime.CompilerServices.Unsafe.dll ^
|
||||
%TargetDir%TraceReloggerLib.dll
|
||||
|
||||
DEL /f %TargetDir%*.dll >NUL 2>&1
|
||||
MOVE /Y %TargetDir%NetchMerged.exe %TargetDir%Netch.exe >NUL
|
||||
)
|
||||
|
||||
RD /S /Q %TargetDir%bin >NUL 2>&1
|
||||
RD /S /Q %TargetDir%i18n >NUL 2>&1
|
||||
RD /S /Q %TargetDir%mode >NUL 2>&1
|
||||
|
||||
XCOPY /s /Y %SolutionDir%binaries %TargetDir%bin\ >NUL
|
||||
XCOPY /s /Y %SolutionDir%translations\i18n %TargetDir%i18n\ >NUL
|
||||
XCOPY /s /Y %SolutionDir%modes\mode %TargetDir%mode\ >NUL
|
||||
|
||||
DEL /f %TargetDir%*.config >NUL 2>&1
|
||||
DEL /f %TargetDir%*.pdb >NUL 2>&1
|
||||
RD /s /Q %TargetDir%x86 >NUL 2>&1
|
||||
52
Netch/Properties/Resources.Designer.cs
generated
@@ -1,10 +1,10 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -13,13 +13,13 @@ namespace Netch.Properties {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
@@ -33,7 +33,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
@@ -47,8 +47,8 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性
|
||||
/// 重写当前线程的 CurrentUICulture 属性。
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
@@ -61,17 +61,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static byte[] CNIP {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CNIP", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap CopyLink {
|
||||
get {
|
||||
@@ -81,7 +71,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] defaultTUNTAP {
|
||||
get {
|
||||
@@ -91,7 +81,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap delete {
|
||||
get {
|
||||
@@ -101,7 +91,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap edit {
|
||||
get {
|
||||
@@ -111,7 +101,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Netch {
|
||||
get {
|
||||
@@ -121,7 +111,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap speed {
|
||||
get {
|
||||
@@ -131,7 +121,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Sponsor {
|
||||
get {
|
||||
@@ -141,7 +131,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] zh_CN {
|
||||
get {
|
||||
|
||||
@@ -133,9 +133,6 @@
|
||||
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="CNIP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CNIP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
||||
5695
Netch/Resources/CNIP
@@ -165,6 +165,7 @@
|
||||
"Port value illegal. Try again.": "端口值非法。请重试。",
|
||||
"Check update when opened": "打开软件时检查更新",
|
||||
"Check Beta update": "检查 Beta 更新",
|
||||
"Update subscribeat when opened": "自动更新订阅",
|
||||
"SS DLL(No ACL support)": "SS DLL(不支持 ACL)",
|
||||
"Modify System DNS": "修改系统 DNS",
|
||||
"ProfileCount": "快捷配置数量",
|
||||
|
||||
@@ -6,14 +6,13 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Bandwidth
|
||||
{
|
||||
public static int received;
|
||||
public static ulong received;
|
||||
public static TraceEventSession tSession;
|
||||
|
||||
/// <summary>
|
||||
@@ -21,7 +20,7 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
/// <param name="bandwidth">流量</param>
|
||||
/// <returns>带单位的流量字符串</returns>
|
||||
public static string Compute(long size)
|
||||
public static string Compute(ulong size)
|
||||
{
|
||||
var mStrSize = @"0";
|
||||
const double step = 1024.00;
|
||||
@@ -50,33 +49,37 @@ namespace Netch.Utils
|
||||
return mStrSize;
|
||||
}
|
||||
|
||||
public static bool NetTrafficAvailable => /*Global.Settings.EnableNetTraffic && */Environment.OSVersion.Version.Major >= 10;
|
||||
|
||||
/// <summary>
|
||||
/// 根据程序名统计流量
|
||||
/// </summary>
|
||||
/// <param name="ProcessName"></param>
|
||||
public static void NetTraffic(Server server, Mode mode, ref MainController mainController)
|
||||
public static void NetTraffic(Server server, Mode mode)
|
||||
{
|
||||
if (!NetTrafficAvailable)
|
||||
return;
|
||||
|
||||
var counterLock = new object();
|
||||
//int sent = 0;
|
||||
|
||||
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
|
||||
var instances = new List<Process>();
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.Name == "HTTP")
|
||||
if (server.Type.Equals("Socks5") && MainController.ModeController.Name == "HTTP")
|
||||
{
|
||||
instances.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance);
|
||||
instances.Add(((HTTPController) MainController.ModeController).pPrivoxyController.Instance);
|
||||
}
|
||||
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL &&
|
||||
(mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
|
||||
{
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
}
|
||||
else if (mainController.pEncryptedProxyController != null)
|
||||
else if (MainController.EncryptedProxyController != null)
|
||||
{
|
||||
instances.Add(mainController.pEncryptedProxyController.Instance);
|
||||
instances.Add(MainController.EncryptedProxyController.Instance);
|
||||
}
|
||||
else if (mainController.pModeController != null)
|
||||
else if (MainController.ModeController != null)
|
||||
{
|
||||
instances.Add(mainController.pModeController.Instance);
|
||||
instances.Add(MainController.ModeController.Instance);
|
||||
}
|
||||
|
||||
var processList = instances.Select(instance => instance.Id).ToList();
|
||||
@@ -96,7 +99,7 @@ namespace Netch.Utils
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
lock (counterLock)
|
||||
received += data.size;
|
||||
received += (ulong) data.size;
|
||||
|
||||
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
|
||||
}
|
||||
@@ -106,7 +109,7 @@ namespace Netch.Utils
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
lock (counterLock)
|
||||
received += data.size;
|
||||
received += (ulong) data.size;
|
||||
|
||||
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
|
||||
}
|
||||
@@ -126,7 +129,7 @@ namespace Netch.Utils
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (tSession != null) tSession.Dispose();
|
||||
tSession?.Dispose();
|
||||
received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace Netch.Utils
|
||||
|
||||
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
|
||||
|
||||
public static string GetRelativePath(string fullName) => fullName.Substring(ModeDirectory.Length);
|
||||
public static string GetFullPath(string relativeName) => Path.Combine(ModeDirectory, relativeName);
|
||||
public static string GetFullPath(Mode mode) => Path.Combine(ModeDirectory, mode.RelativePath);
|
||||
|
||||
/// <summary>
|
||||
/// 从模式文件夹读取模式并为 <see cref="Forms.MainForm.ModeComboBox"/> 绑定数据
|
||||
/// </summary>
|
||||
@@ -43,15 +47,15 @@ namespace Netch.Utils
|
||||
Sort();
|
||||
}
|
||||
|
||||
private static void LoadModeFile(string path)
|
||||
private static void LoadModeFile(string fullName)
|
||||
{
|
||||
var mode = new Mode
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(path),
|
||||
RelativePath = path.Substring(ModeDirectory.Length)
|
||||
FileName = Path.GetFileNameWithoutExtension(fullName),
|
||||
RelativePath = GetRelativePath(fullName)
|
||||
};
|
||||
|
||||
var content = File.ReadAllLines(path);
|
||||
var content = File.ReadAllLines(fullName);
|
||||
if (content.Length == 0) return;
|
||||
|
||||
for (var i = 0; i < content.Length; i++)
|
||||
@@ -67,8 +71,8 @@ namespace Netch.Utils
|
||||
if ((tmp = splited.ElementAtOrDefault(0)) != null)
|
||||
mode.Remark = i18N.Translate(tmp);
|
||||
|
||||
if ((tmp = splited.ElementAtOrDefault(1)) != null)
|
||||
mode.Type = int.Parse(tmp);
|
||||
tmp = splited.ElementAtOrDefault(1);
|
||||
mode.Type = tmp != null ? int.Parse(tmp) : 0;
|
||||
|
||||
if ((tmp = splited.ElementAtOrDefault(2)) != null)
|
||||
mode.BypassChina = int.Parse(tmp) == 1;
|
||||
@@ -88,6 +92,25 @@ namespace Netch.Utils
|
||||
Global.Modes.Add(mode);
|
||||
}
|
||||
|
||||
public static void WriteFile(Mode mode)
|
||||
{
|
||||
if (!Directory.Exists(ModeDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(ModeDirectory);
|
||||
}
|
||||
|
||||
var fullName = GetFullPath(mode.RelativePath ?? mode.FileName + ".txt");
|
||||
|
||||
if (mode.RelativePath == null && File.Exists(fullName))
|
||||
{
|
||||
throw new Exception("新建模式的文件名已存在,请贡献者检查代码");
|
||||
}
|
||||
|
||||
// 写入到模式文件里
|
||||
File.WriteAllText(fullName, mode.ToFileString());
|
||||
mode.RelativePath = GetRelativePath(fullName);
|
||||
}
|
||||
|
||||
private static void Sort()
|
||||
{
|
||||
Global.Modes.Sort((a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
@@ -102,7 +125,12 @@ namespace Netch.Utils
|
||||
|
||||
public static void Delete(Mode mode)
|
||||
{
|
||||
mode.DeleteFile();
|
||||
var fullName = GetFullPath(mode);
|
||||
if (File.Exists(fullName))
|
||||
{
|
||||
File.Delete(fullName);
|
||||
}
|
||||
|
||||
Global.Modes.Remove(mode);
|
||||
Global.MainForm.InitMode();
|
||||
}
|
||||
|
||||
2
binaries
@@ -1,9 +1,5 @@
|
||||
# Netch 模式
|
||||
|
||||
用于存储 Netch 模式文件的仓库
|
||||
|
||||
|
||||
|
||||
## 目录
|
||||
|
||||
1. [模式介绍](#模式介绍)
|
||||
@@ -41,9 +37,7 @@
|
||||
- 底层依赖于 [NetFilter SDK](https://netfiltersdk.com) 和 Redirector.exe(未开源)等
|
||||
- 对于第一次使用 Netch 的用户而言,不需要做多余的事情
|
||||
- 若 [NetFilter SDK](https://netfiltersdk.com) 的驱动不存在,会自动安装
|
||||
- 自动安装驱动时不会判断旧驱动是否需要更新
|
||||
- 对于老用户而言,版本更新日志里如果提到要更新驱动,或者你发现无法使用本模式时,可以通过运行 `DriverUpdater.exe` 的方式强制覆盖旧驱动
|
||||
- 相关代码 [NFController.cs](..\Netch\Controllers\NFController.cs)
|
||||
- 若驱动版本过低,会自动更新
|
||||
|
||||
范例文件
|
||||
|
||||
@@ -159,7 +153,7 @@ V2Ray
|
||||
|
||||
当前版本已添加配置编辑功能,根据自己的情况,使用订阅或者别的方法添加代理配置,我这里使用的是剪贴板导入
|
||||
|
||||

|
||||

|
||||
|
||||
如果你发现你的程序没我截图的看起来清晰,可以右键 `Netch.exe - 属性 - 兼容性 - 更改高 DPI 设置 - 替代高 DPI 缩放执行 - 系统(增强)`
|
||||
|
||||
@@ -173,24 +167,24 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
|
||||
|
||||
接着点击菜单栏上的`模式 - 创建进程模式`
|
||||
|
||||

|
||||

|
||||
|
||||
### 扫描
|
||||
|
||||
在弹出的窗口中点击`扫描`
|
||||
|
||||

|
||||

|
||||
|
||||
选择你要加速的游戏的安装路径,根据游戏不同,可能需要选择多个不同的目录进行扫描,参见[萌鹰的 Netch 教程](https://www.eaglemoe.com/archives/142)(包括 GTAOL 和 R6S 的配置方法)
|
||||
|
||||
>4. 选定 GTA5 游戏目录,点击确定,软件会自动扫描目录下的 exe 程式并填写进去
|
||||
>5. 再次点击扫描,选择 SocialClub 的安装地址(一般为 C:\Program Files\Rockstar Games\Social Club),点击确定,点击保存
|
||||
>
|
||||
>注意:加入游戏时请不要忘记加入社交组件,比如说 GTA 不要忘记 SocialClub ,彩虹六号不要忘记 Uplay
|
||||
>注意:加入游戏时请不要忘记加入社交组件,比如说 GTA 不要忘记 SocialClub ,彩虹六号不要忘记 Uplay,如果游戏进程名与其他进程名重复,则可手动修改已创建好的模式文件,在进程名前加上绝对路径即可。csgo.exe -> C:\steam\game\Counter-Strike Global Offensive\csgo.exe
|
||||
|
||||
这里以战争雷霆为例,只需添加战争雷霆游戏根目录即可
|
||||
这里以CSGO为例,只需添加CSGO游戏根目录即可
|
||||
|
||||

|
||||

|
||||
|
||||
扫描时可能需要稍等片刻,扫描后记得填写备注
|
||||
|
||||
@@ -198,13 +192,13 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
|
||||
|
||||
之后点保存进行`保存`
|
||||
|
||||

|
||||

|
||||
|
||||
### 启动
|
||||
|
||||
最后确认服务器一栏和模式一栏均为之前自己添加并需要使用的,没问题后点击`启动`即可
|
||||
|
||||

|
||||

|
||||
|
||||
启动后,你再去游戏根目录或者别的启动器如 Steam,Uplay 启动游戏即可。此时游戏就已经被代理了
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 新手入门
|
||||
**Version : 1.4.10**
|
||||
**Version : 1.5.1**
|
||||
|
||||
[下载地址](https://github.com/NetchX/Netch/releases)
|
||||
|
||||
@@ -7,36 +7,42 @@
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 设置界面
|
||||
|
||||

|
||||
|
||||
## 添加服务器
|
||||
|
||||
> Netch 目前仅支持以下代理协议:Shadowsocks,VMess,Socks5,ShadowsockR,Trojan。
|
||||
|
||||
首先,点击`服务器`增加所需服务器
|
||||
|
||||
<img width="50%" height="50%" src="screenshots/addServer.zh-CN.png">
|
||||
<img width="80%" height="80%" src="screenshots/addServer.zh-CN.png">
|
||||
|
||||
可手动添加单个服务器,或者通过剪切板链接添加单个服务器。也可通过订阅链接批量添加。
|
||||
|
||||
点击 `订阅` ` 管理订阅链接` 进入以下界面。
|
||||
|
||||
<img width="50%" height="50%" src="screenshots/addLink.zh-CN.png">
|
||||
<img width="80%" height="80%" src="screenshots/addLink.zh-CN.png">
|
||||
|
||||
填写备注与链接,点击添加,然后保存。保存后点击 `订阅` ` 从订阅链接更新服务器`。完成服务器添加。添加完服务器后可对服务器进行修改,删除和测速。
|
||||
填写备注与链接,然后保存即可。保存后在主界面点击 `订阅` ` 从订阅链接更新服务器`。完成服务器添加。添加完服务器后可对服务器进行修改,删除和测速。
|
||||
|
||||
## 选择模式
|
||||
|
||||
> 此处需要会一点英语,比如你应该知道 `吃鸡` 的英文名称是 `PlayerUnknown's Battlegrounds`
|
||||
|
||||
1.3.7 上线了模式搜索功能,即在模式框里输入字符即可搜索,使用英文名称进行搜索,搜索到所需的模式后单击选择,启用模式。相对应的游戏即可被加速
|
||||
模式选择框有搜索功能,在模式框里输入字符即可搜索,使用英文名称进行搜索,搜索到所需的模式后单击选择,启用模式。相对应的游戏即可被加速
|
||||
|
||||
若没有所需的模式,请选择 `[3] Bypass LAN and China (TUN/TAP)` 的模式。此模式需要安装 [TAP-Windows](https://github.com/OpenVPN/tap-windows) 适配器,如果 Netch 提示没有该适配器,可以直接安装 [TAP-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 来获得该适配器
|
||||
|
||||
关于更多的模式说明,详见 [进阶用法](https://github.com/NormanBB/NetchMode/blob/master/docs/README.zh-CN.md)。
|
||||
关于更多的模式说明,详见 [进阶用法](Advanced_Usage.zh-CN.md)。
|
||||
|
||||
选择完模式后,点击启用,游戏已被代理。这一步需在开启游戏前完成。
|
||||
|
||||
## 配置说明
|
||||
|
||||
目前,Netch 支持自定义四个配置,填入配置名,选择相应的服务器和游戏模式,按下 `Ctrl` 与鼠标左键,即可保存当前配置。下次使用时,点击配置名即可快速启用。
|
||||
在设置界面填写完快捷配置数量后即可在主界面进行配置,填入配置名,选择相应的服务器和游戏模式,按下 `Ctrl` 与鼠标左键,即可保存当前配置。下次使用时,点击配置名即可快速启用。
|
||||
|
||||
~~ 如果你还觉得不会用,可以去用 SSTap (逃~~
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
BIN
docs/screenshots/advanced/createMode.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/screenshots/advanced/importServer.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/screenshots/advanced/modeForm.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
docs/screenshots/advanced/saveMode.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/screenshots/advanced/scan.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/screenshots/advanced/started.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
BIN
docs/screenshots/main.zh-CN2.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshots/set.png
Normal file
|
After Width: | Height: | Size: 64 KiB |