Compare commits

...

61 Commits

Author SHA1 Message Date
AmazingDM
8e1d70c29e 1.5.1 更新了一波文档 2020-09-04 13:52:00 +08:00
AmazingDM
a47395cc6c 打开软件时自动更新订阅; 2020-09-04 11:11:59 +08:00
AmazingDM
e3bc370019 update v2ray to 4.27.5 2020-09-04 10:47:13 +08:00
ChsBuffer
825b7bd6a6 fix typo 2020-09-02 15:46:05 +08:00
ChsBuffer
2705f5c160 日志输出路由表添加失败项 2020-09-02 15:41:11 +08:00
ChsBuffer
fd8185b83e 修复个别路由表添加失败导致过程中断的问题 2020-09-02 14:58:43 +08:00
ChsBuffer
b3866d1d90 CNIP 外置 2020-09-02 13:47:38 +08:00
ChsBuffer
bb2fbfaafa 还原创建进程模式扫描目录的选择文件夹对话框 2020-09-02 13:44:04 +08:00
ChsBuffer
efa1b2ed54 Windows 10 以下禁用流量统计 2020-08-31 17:49:03 +08:00
ChsBuffer
60aa010096 修复Socks5服务器状态栏显示错误,主控制器转为静态类 2020-08-31 17:47:37 +08:00
ChsBuffer
68880f0c9b 创建 NAT 测试方法记录是否测试完毕,改进重新测试NAT 2020-08-29 22:14:07 +08:00
ChsBuffer
7310126a39 PostBuild 命令移至脚本,仅 Release 合并 dll 2020-08-29 15:37:19 +08:00
ChsBuffer
389ac32094 合并dll 2020-08-29 14:17:35 +08:00
ChsBuffer
2676e97ca0 重复启动Netch不清除日志 2020-08-29 12:29:19 +08:00
ChsBuffer
1c231ee897 重写模式保存 2020-08-29 12:06:32 +08:00
ChsBuffer
549988b2de 订阅管理界面使用Panel布局 2020-08-29 10:45:49 +08:00
ChsBuffer
18963fa14b 改进NTT异常显示 2020-08-29 10:26:14 +08:00
ChsBuffer
81da544108 STUN 服务器选择使用 ComboBox 而不是 SearchComboBox 2020-08-29 05:59:27 +08:00
AmazingDM
d88d1b2e0c 流量统计改为ulong 修复超过2g爆炸的问题 2020-08-28 14:55:04 +08:00
AmazingDM
a4081b1a7f 点击NAT label和状态灯即可重测NAT 2020-08-28 13:58:31 +08:00
Bruce Wayne
f76a1f1a8e Merge pull request #353 from HMBSbige/issue
Add issue config
2020-08-28 12:40:04 +08:00
Bruce Wayne
d60716cf4f Add issue config 2020-08-28 12:39:04 +08:00
Connection Refused
91a0ad5424 Version 1.5.0 released 2020-08-28 10:36:08 +08:00
ChsBuffer
bb3bfaaffc 修复添加服务器ServerComboBox未刷新 2020-08-26 14:50:39 +08:00
AmazingDM
1a19f59a6b eee 2020-08-26 14:30:02 +08:00
AmazingDM
e68929a32a 🐛修复优化流量统计
🐛启动时保存配置文件,防止直接关闭程序时服务器模式index丢失(debug用)
🐛日志输出空异常修复
2020-08-26 14:23:03 +08:00
ChsBuffer
c274360cc2 Updater 内置 7za 以支持 7z 更新包 2020-08-26 14:05:08 +08:00
ChsBuffer
5755230000 修复 Modes 添加删除不更新 ModeComboBox,从剪切板导入服务器 ServerComboBox
不更新
2020-08-25 18:30:27 +08:00
ChsBuffer
388896f161 修复模式,服务器变更后 ComboBox 未更新 2020-08-25 06:26:27 +08:00
ChsBuffer
9b4304f016 MainForm_VisibleChanged 移至 SettingForm 关闭后 2020-08-25 05:01:43 +08:00
ChsBuffer
b061a00057 还原 "ComboBox 绑定数据" 2020-08-25 04:57:31 +08:00
AmazingDM
41751db06f Merge pull request #351 from HMBSbige/CI
Fix CI
2020-08-25 00:47:37 +08:00
Bruce Wayne
e4927c4d0c Fix CI 2020-08-25 00:26:43 +08:00
AmazingDM
aef01473ce Merge pull request #350 from HMBSbige/CI
Update CI
2020-08-25 00:05:59 +08:00
Bruce Wayne
a280391ba9 Update CI 2020-08-24 23:47:17 +08:00
ChsBuffer
fe3b69a267 Fixed a typo in saving alterId in Vmess form 2020-08-22 09:52:35 +08:00
Connection Refused
35589138d5 Update README.zh-CN.md 2020-08-22 02:06:53 +08:00
Connection Refused
31c000db62 Update README.md 2020-08-22 02:06:14 +08:00
ChsBuffer
ca62e2200d Fix null reference error using socks5 server 2020-08-21 01:17:17 +08:00
ChsBuffer
6c1c2a2b44 ComboBox 绑定数据 2020-08-20 15:28:45 +08:00
ChsBuffer
2dfc23bbac 分离读取模式文件夹方法,修改为等待子窗口退出后显示 2020-08-19 21:47:50 +08:00
ChsBuffer
8d2890790e 编译脚本简化,修复加密代理控制器停止缓存日志未写入文件,修复更新0条订阅不提示 2020-08-19 00:13:18 +08:00
ChsBuffer
638714804b 修改 添加/编辑服务器 代码,Vmess默认开启MUX多路复用 2020-08-16 07:30:18 +08:00
ChsBuffer
d90a48577c 修复unbound日志未重定向 2020-08-16 07:28:46 +08:00
ChsBuffer
bd5cf8b70d 分流预备工作 2020-08-15 03:36:58 +08:00
ChsBuffer
d2c911da51 还原控制器日志写入方法 2020-08-14 11:02:52 +08:00
AmazingDM
c30c554c8f 添加NTT日志 2020-08-14 10:00:41 +08:00
ChsBuffer
21ad84beb3 Update NetchUpdater.csproj to new project format 2020-08-14 04:17:59 +08:00
ChsBuffer
9943207e1f Reduce Reference and cleanup 2020-08-14 04:14:44 +08:00
ChsBuffer
4116daf63c built-in stun server list 2020-08-13 12:50:20 +08:00
ChsBuffer
a2838b8217 修复进程编辑模式文件名会被更改,添加编辑模式不可用提醒,抽取进程模式编辑右键菜单 2020-08-13 01:06:55 +08:00
ChsBuffer
c490e9f802 重写WebUtil 2020-08-12 22:43:24 +08:00
ChsBuffer
1f520b2f8f 下载更新异常捕捉 2020-08-12 22:05:53 +08:00
ChsBuffer
41c9269c9a 补充中文翻译 2020-08-12 02:47:56 +08:00
ChsBuffer
be2ee1a5df 自动下载并更新 2020-08-12 02:22:55 +08:00
AmazingDM
6f1025c73e 更新NTT类型为数字显示
1.FullCone
2.RestrictedCone
3.PortRestrictedCone
4.Symmetric
2020-08-11 17:39:30 +08:00
Amazing_DM
24f8691cf9 更新 NTT
更新 V2ray  to v4.27.0
2020-08-11 12:11:34 +08:00
ChsBuffer
3933319e32 修复pre-releases比release新的问题 2020-08-11 03:40:33 +08:00
ChsBuffer
e8fbde707b 修复TUN/TAP启动时会被无关适配器的异常所中断导致启动失败 2020-08-10 23:34:48 +08:00
ChsBuffer
e60f8e7c4a 订阅管理实现编辑选中节点 2020-08-10 06:56:37 +08:00
ChsBuffer
91a484dfcd 移除进程模式规则保存为时间戳 2020-08-10 06:33:35 +08:00
87 changed files with 2520 additions and 7380 deletions

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View 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

View File

@@ -1,5 +1,5 @@
name: Netch CI
on: [push]
on: [push, pull_request]
jobs:
build:
name: Build
@@ -13,9 +13,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Update submodules
uses: snickerbockers/submodules-init@v4
with:
submodules: true
- name: Restore NuGet Packages Cache
uses: actions/cache@v2
@@ -28,17 +27,18 @@ jobs:
- name: Restore NuGet Package
run: nuget restore Netch.sln
- name: Build .NET 4.8
- name: Build Solution
shell: pwsh
run: |
.\BUILD.ps1
New-Item -ItemType Directory -Path C:\builtfiles -Force > $null
Compress-Archive -Path Netch\bin\x64\Release\win-x64\* -DestinationPath C:\builtfiles\Netch.zip
echo "::set-env name=Netch_SHA256::$(.\GetSHA256.ps1 C:\builtfiles\Netch.zip)"
7z a -mx9 C:\builtfiles\Netch.7z .\Netch\bin\x64\Release\win-x64\
7z rn C:\builtfiles\Netch.7z win-x64 Netch
echo "::set-env name=Netch_SHA256::$(.\GetSHA256.ps1 C:\builtfiles\Netch.7z)"
echo "::set-env name=Netch_EXE_SHA256::$(.\GetSHA256.ps1 Netch\bin\x64\Release\win-x64\Netch.exe)"
- name: Upload
uses: actions/upload-artifact@v1
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: Netch
path: Netch\bin\x64\Release\win-x64
@@ -53,17 +53,17 @@ jobs:
prerelease: true
draft: false
files: |
C:\builtfiles\Netch.zip
C:\builtfiles\Netch.7z
body: |
[![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/Netch) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/Netch_Discuss_Group)
## 更新日志
* 这是 GitHub Action 自动化部署,更新日志应该很快会手动更新
* 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新
## 校验和
| 文件名 | SHA256 |
| :- | :- |
| Netch.zip | ${{ env.Netch_SHA256 }} |
| Netch.7z | ${{ env.Netch_SHA256 }} |
# Deploy:
# needs: [build]

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/.vs
/packages
/packages
.idea/

View File

@@ -16,19 +16,17 @@ function Build-NetFrameworkx64
$outdir = "$net_baseoutput\x64"
msbuild -v:m -m -t:Build /p:Configuration="Release" /p:Platform="x64" /p:TargetFramework=net48 /p:Runtimeidentifier=win-x64 /restore
msbuild -v:n -m:1 /p:Configuration="Release" `
/p:Platform="x64" `
/p:TargetFramework=net48 `
/p:Runtimeidentifier=win-x64 `
/restore
if ($LASTEXITCODE) { cd $mainDir ; exit $LASTEXITCODE }
Write-Host 'Build x64 Completed, start copy bin, mode, i18n file'
Remove-Item -Recurse -Force "$net_baseoutput\x64\Release\win-x64\bin\tap-driver"
Copy-Item -Recurse "$mainDir\binaries\*" "$net_baseoutput\x64\Release\win-x64\bin"
Copy-Item -Recurse "$mainDir\modes\mode\*" "$net_baseoutput\x64\Release\win-x64\mode"
Copy-Item -Recurse "$mainDir\translations\i18n\*" "$net_baseoutput\x64\Release\win-x64\i18n"
Write-Host 'Build done'
}
cd $mainDir\Netch
cd $mainDir
Build-NetFrameworkx64
cd $mainDir

View File

@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchLib", "NetchLib\NetchLib.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchUpdater", "NetchUpdater\NetchUpdater.csproj", "{828318A8-9B90-4A5F-BD6B-E632CC9D8933}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -21,6 +23,10 @@ Global
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Debug|x64.Build.0 = Debug|x64
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.ActiveCfg = Release|x64
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.Build.0 = Release|x64
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Debug|x64.ActiveCfg = Debug|x64
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Debug|x64.Build.0 = Debug|x64
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.ActiveCfg = Release|x64
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -9,7 +9,7 @@ namespace Netch.Controllers
{
Name = "DNS Service";
MainFile = "unbound.exe";
RedirectStd = false;
// RedirectStd = false;
}
/// <summary>

View File

@@ -1,5 +1,4 @@
using System.Text;
using System.Threading;
using Netch.Models;
using Netch.Utils;
@@ -7,6 +6,8 @@ namespace Netch.Controllers
{
public class SSController : EncryptedProxy
{
private bool dllFlag = false;
public SSController()
{
Name = "Shadowsocks";
@@ -20,8 +21,9 @@ namespace Netch.Controllers
//从DLL启动Shaowsocks
if (Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
{
dllFlag = true;
State = State.Starting;
var client = Encoding.UTF8.GetBytes($"0.0.0.0:{Global.Settings.Socks5LocalPort}");
var client = Encoding.UTF8.GetBytes($"{LocalAddress}:{Socks5LocalPort}");
var remote = Encoding.UTF8.GetBytes($"{server.Hostname}:{server.Port}");
var passwd = Encoding.UTF8.GetBytes($"{server.Password}");
var method = Encoding.UTF8.GetBytes($"{server.EncryptMethod}");
@@ -49,7 +51,8 @@ namespace Netch.Controllers
#region Argument
var argument = new StringBuilder();
argument.Append($"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
argument.Append(
$"-s {server.Hostname} -p {server.Port} -b {LocalAddress} -l {Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
argument.Append($" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"");
if (mode.BypassChina) argument.Append(" --acl default.acl");
@@ -64,7 +67,7 @@ namespace Netch.Controllers
/// </summary>
public override void Stop()
{
if (Global.Settings.BootShadowsocksFromDLL) NativeMethods.Shadowsocks.Stop();
if (dllFlag) NativeMethods.Shadowsocks.Stop();
else
StopInstance();
}

View File

@@ -33,7 +33,7 @@ namespace Netch.Controllers
if (!string.IsNullOrEmpty(server.OBFSParam)) argument.Append($" -g \"{server.OBFSParam}\"");
}
argument.Append($" -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -u");
argument.Append($" -b {LocalAddress} -l {Socks5LocalPort} -u");
if (mode.BypassChina) argument.Append(" --acl default.acl");
#endregion

View File

@@ -22,8 +22,8 @@ namespace Netch.Controllers
{
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
{
local_addr = Global.Settings.LocalAddress,
local_port = Global.Settings.Socks5LocalPort,
local_addr = LocalAddress,
local_port = Socks5LocalPort,
remote_addr = server.Hostname,
remote_port = server.Port,
password = new List<string>

View File

@@ -27,8 +27,8 @@ namespace Netch.Controllers
new VMess.Inbounds
{
settings = new VMess.InboundSettings(),
port = Global.Settings.Socks5LocalPort,
listen = Global.Settings.LocalAddress
port = Socks5LocalPort,
listen = LocalAddress
}
},
outbounds = new List<VMess.Outbounds>

View File

@@ -1,9 +1,26 @@
using Netch.Models;
using Netch.Utils;
namespace Netch.Controllers
{
public abstract class EncryptedProxy : Controller
{
private int? _socks5Port;
public int Socks5LocalPort
{
get => _socks5Port ?? Global.Settings.Socks5LocalPort;
set => _socks5Port = value;
}
private string _localAddress;
public string LocalAddress
{
get => _localAddress ?? Global.Settings.LocalAddress;
set => _localAddress = value;
}
/// <summary>
/// 启动
/// </summary>

View File

@@ -44,7 +44,7 @@ namespace Netch.Controllers
/// </summary>
private string _logPath;
private FileStream _logFileStream;
private readonly StringBuilder _logBuffer = new StringBuilder();
/// <summary>
/// 程序输出的编码,
@@ -92,8 +92,7 @@ namespace Netch.Controllers
RedirectStandardOutput = RedirectStd,
UseShellExecute = !RedirectStd,
WindowStyle = ProcessWindowStyle.Hidden
},
EnableRaisingEvents = true
}
};
}
@@ -111,13 +110,13 @@ namespace Netch.Controllers
{
// 初始化程序
InitInstance(argument);
Instance.EnableRaisingEvents = true;
if (RedirectStd)
{
// 清理日志
_logPath ??= Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
if (_logFileStream == null && File.Exists(_logPath))
if (File.Exists(_logPath))
File.Delete(_logPath);
_logFileStream = new FileStream(_logPath, FileMode.Create, FileAccess.Write);
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
@@ -129,12 +128,13 @@ namespace Netch.Controllers
Instance.Start();
if (priority != ProcessPriorityClass.Normal)
Instance.PriorityClass = priority;
if (!RedirectStd || StartedKeywords.Count == 0) return true;
if (!RedirectStd) return true;
// 启动日志重定向
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
_writeStreamTimer.Elapsed += SaveStreamTimerEvent;
_writeStreamTimer.Enabled = true;
SaveBufferTimer.Elapsed += SaveBufferTimerEvent;
SaveBufferTimer.Enabled = true;
if (StartedKeywords.Count == 0) return true;
// 等待启动
for (var i = 0; i < 1000; i++)
{
@@ -161,18 +161,17 @@ namespace Netch.Controllers
}
}
private static System.Timers.Timer _writeStreamTimer = new System.Timers.Timer(300) {AutoReset = true};
private static readonly System.Timers.Timer SaveBufferTimer = new System.Timers.Timer(300) {AutoReset = true};
private void OnExited(object sender, EventArgs e)
{
if (RedirectStd)
{
_writeStreamTimer.Enabled = false;
Thread.Sleep(100); // 等待 Write() 写入流
_logFileStream.Close();
_logFileStream = null;
SaveBufferTimer.Enabled = false;
}
SaveBufferTimerEvent(null, null);
State = State.Stopped;
}
@@ -187,9 +186,9 @@ namespace Netch.Controllers
if (e.Data == null)
return;
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data + Global.EOF);
Task.Run(() => Write(info));
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data);
var str = Encoding.UTF8.GetString(info);
Write(str);
// 检查启动
if (State == State.Starting)
{
@@ -205,42 +204,30 @@ namespace Netch.Controllers
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void SaveStreamTimerEvent(object sender, EventArgs e)
private void SaveBufferTimerEvent(object sender, EventArgs e)
{
if (_logFileStream == null) return;
try
{
await _logFileStream.FlushAsync();
if (_logPath != null && _logBuffer != null)
{
File.AppendAllText(_logPath, _logBuffer.ToString());
_logBuffer.Clear();
}
}
catch
catch (Exception exception)
{
// ignored
Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
}
}
private readonly object _fileLocker = new object();
/// <summary>
/// 写入日志文件
/// 写入日志文件缓冲
/// </summary>
/// <param name="info"></param>
/// <returns>转码后的字符串</returns>
private void Write(byte[] info)
private void Write(string info)
{
if (info == null)
return;
try
{
lock (_fileLocker)
{
_logFileStream.Write(info, 0, info.Length);
}
}
catch (Exception e)
{
Logging.Error($"写入 {Name} 日志错误:\n" + e.Message);
}
_logBuffer.Append(info + Global.EOF);
}
}
}

View File

@@ -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(Global.Settings.Socks5LocalPort); // 记录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);
}

View File

@@ -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>
@@ -285,58 +285,63 @@ namespace Netch.Controllers
}
}
// 根据 IP Index 寻找 出口适配器
var errorAdaptersId = new List<string>();
try
{
try
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ =>
{
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.GetIPProperties().GetIPv4Properties().Index == Global.Adapter.Index);
Global.Adapter.Address = adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
Logging.Info($"出口 IPv4 地址:{Global.Adapter.Address}");
Logging.Info($"出口 网关 地址:{Global.Adapter.Gateway}");
Logging.Info($"出口适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.Adapter.Index}");
// Ex NetworkInformationException: 此接口不支持 IPv4 协议。
// Ex NetworkInformationException: Windows 系统函数调用失败。
// Ex System.ArgumentNullException: source 或 predicate 为 null。
}
catch (Exception e)
{
if (e is InvalidOperationException)
Logging.Error($"找不到网络接口索引为 {Global.Adapter.Index} 的出口适配器");
throw;
}
try
{
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
}
catch (Exception e)
{
if (e is InvalidOperationException)
Logging.Error($"找不到标识符为 {Global.TUNTAP.ComponentID} 的 TAP 适配器");
throw;
}
return true;
try
{
return _.GetIPProperties().GetIPv4Properties().Index == Global.Adapter.Index;
}
catch (NetworkInformationException)
{
errorAdaptersId.Add(_.Id);
return false;
}
});
Global.Adapter.Address = adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
Logging.Info($"出口 IPv4 地址:{Global.Adapter.Address}");
Logging.Info($"出口 网关 地址:{Global.Adapter.Gateway}");
Logging.Info($"出口适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.Adapter.Index}");
}
catch (InvalidOperationException)
catch (Exception e)
{
Logging.Error($"找不到 IP Index 为 {Global.Adapter.Index} 的出口适配器: {e.Message}");
PrintAdapters();
return false;
}
// 根据 ComponentID 寻找 Tap适配器
try
{
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
}
catch (Exception e)
{
var msg = e switch
{
InvalidOperationException _ => $"找不到标识符为 {Global.TUNTAP.ComponentID} 的 TAP 适配器: {e.Message}",
NetworkInformationException _ => $"获取 Tap 适配器信息错误: {e.Message}",
_ => $"Tap 适配器其他异常: {e}"
};
Logging.Error(msg);
PrintAdapters();
return false;
}
return true;
void PrintAdapters()
{
// 理论上如果得到了网络接口的索引/网络适配器的标识符不会找不到网卡
// 只是异常处理
Logging.Info("所有适配器:\n" +
NetworkInterface.GetAllNetworkInterfaces().Aggregate(string.Empty, (current, adapter)
=> current + $"{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}{Global.EOF}"));
return false;
}
catch (NetworkInformationException e)
{
if (e.ErrorCode == 10043)
MessageBoxX.Show("适配器未开启IPv4协议", LogLevel.ERROR, owner: Global.MainForm);
return false;
=> current + $"{ /*如果加了 * 代表遍历中出现异常的适配器 */(errorAdaptersId.Contains(adapter.Id) ? "*" : "")}{adapter.Name} {adapter.Id} {adapter.Description}{Global.EOF}"));
}
}
@@ -353,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)
@@ -381,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;
}
}
}

View File

@@ -1,13 +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 _lastResult;
private string _localEnd;
private string _publicEnd;
private string _natType;
private bool _nttResult;
public NTTController()
{
@@ -21,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}");
@@ -30,13 +37,7 @@ namespace Netch.Controllers
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
Instance.WaitForExit();
var result = _lastResult.Split('#');
var natType = result[0];
var localEnd = result[1];
var publicEnd = result[2];
return (true, natType, localEnd, publicEnd);
return (_nttResult, _natType, _localEnd, _publicEnd);
}
catch (Exception e)
{
@@ -56,8 +57,35 @@ namespace Netch.Controllers
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
_lastResult = e.Data;
if (string.IsNullOrEmpty(e.Data)) return;
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];
switch (key)
{
case "Other address is":
case "Binding test":
case "Nat mapping behavior":
case "Nat filtering behavior":
break;
case "Local address":
_localEnd = value;
break;
case "Mapped address":
_publicEnd = value;
break;
case "result":
_natType = value;
_nttResult = true;
break;
default:
_natType = str.Last();
break;
}
}
public override void Stop()

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using Netch.Models.GitHubRelease;
using Netch.Utils;
using Newtonsoft.Json;
@@ -15,43 +15,53 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2020";
public const string Version = @"1.4.12";
public const string Version = @"1.5.1";
public string LatestVersionNumber;
public string LatestVersionUrl;
public string LatestVersionDownloadUrl;
public event EventHandler NewVersionFound;
public event EventHandler NewVersionFoundFailed;
public event EventHandler NewVersionNotFound;
public void Check(bool notifyNoFound, bool isPreRelease)
public async void Check(bool isPreRelease)
{
try
{
var updater = new GitHubRelease(Owner, Repo);
var url = updater.AllReleaseUrl;
var json = WebUtil.DownloadString(WebUtil.CreateRequest(url));
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
var latestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
LatestVersionNumber = latestRelease.tag_name;
LatestVersionUrl = latestRelease.html_url;
LatestVersionDownloadUrl = latestRelease.assets[0].browser_download_url;
Logging.Info($"Github 最新发布版本: {latestRelease.tag_name}");
if (VersionUtil.CompareVersion(latestRelease.tag_name, Version) > 0)
{
LatestVersionNumber = latestRelease.tag_name;
LatestVersionUrl = latestRelease.html_url;
Logging.Info($"发现新版本");
NewVersionFound?.Invoke(this, new EventArgs());
}
else
{
LatestVersionNumber = latestRelease.tag_name;
if (notifyNoFound) NewVersionNotFound?.Invoke(this, new EventArgs());
Logging.Info("目前是最新版本");
NewVersionNotFound?.Invoke(this, new EventArgs());
}
}
catch (Exception e)
{
Logging.Error(e.ToString());
if (notifyNoFound) NewVersionFoundFailed?.Invoke(this, new EventArgs());
if (e is WebException)
Logging.Warning($"获取新版本失败: {e.Message}");
else
{
Logging.Warning(e.ToString());
}
NewVersionFoundFailed?.Invoke(this, new EventArgs());
}
}
}
}
}

View File

@@ -99,7 +99,6 @@
this.Name = "AboutForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "About";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AboutForm_FormClosing);
this.Load += new System.EventHandler(this.AboutForm_Load);
this.SponsorGroupBox.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.SponsorPictureBox)).EndInit();

View File

@@ -19,11 +19,6 @@ namespace Netch.Forms
SponsorGroupBox.Text = i18N.Translate(SponsorGroupBox.Text);
}
private void AboutForm_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
}
private void NetchPictureBox_Click(object sender, EventArgs e)
{
Process.Start("https://github.com/NetchX/Netch");

View File

@@ -125,7 +125,6 @@
this.Name = "GlobalBypassIPForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Global Bypass IPs";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.GlobalBypassIPForm_FormClosing);
this.Load += new System.EventHandler(this.GlobalBypassIPForm_Load);
this.IPGroupBox.ResumeLayout(false);
this.IPGroupBox.PerformLayout();

View File

@@ -28,11 +28,6 @@ namespace Netch.Forms
PrefixComboBox.SelectedIndex = 0;
}
private void GlobalBypassIPForm_FormClosing(object sender, FormClosingEventArgs e)
{
Global.SettingForm.Show();
}
private void AddButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(IPTextBox.Text))

View File

@@ -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;
@@ -18,6 +20,7 @@ namespace Netch.Forms
private async void ControlFun()
{
Configuration.Save();
if (State == State.Waiting || State == State.Stopped)
{
// 服务器、模式 需选择
@@ -41,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)
{
@@ -68,8 +71,8 @@ namespace Netch.Forms
while (State == State.Started)
{
server.Test();
// 重载服务器列表
InitServer();
// 重绘 ServerComboBox
ServerComboBox.Invalidate();
Thread.Sleep(Global.Settings.StartedTcping_Interval * 1000);
}
@@ -86,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;
}
@@ -116,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;
}
}

View File

@@ -41,6 +41,7 @@ namespace Netch.Forms
this.AddVMessServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.AddTrojanServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.HelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -53,8 +54,10 @@ namespace Netch.Forms
this.updateACLWithProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.reinstallTapDriverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CheckForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.AboutToolStripButton = new System.Windows.Forms.ToolStripButton();
this.VersionLabel = new System.Windows.Forms.ToolStripLabel();
this.NewVersionLabel = new System.Windows.Forms.ToolStripLabel();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.configLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
@@ -113,9 +116,11 @@ namespace Netch.Forms
this.ModeToolStripMenuItem,
this.SubscribeToolStripMenuItem,
this.OptionsToolStripMenuItem,
this.HelpToolStripMenuItem,
this.exitToolStripMenuItem,
this.AboutToolStripButton,
this.VersionLabel,
this.exitToolStripMenuItem});
this.NewVersionLabel,
this.VersionLabel});
this.MenuStrip.Location = new System.Drawing.Point(0, 0);
this.MenuStrip.Name = "MenuStrip";
this.MenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
@@ -148,35 +153,35 @@ namespace Netch.Forms
this.AddSocks5ServerToolStripMenuItem.Name = "AddSocks5ServerToolStripMenuItem";
this.AddSocks5ServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
this.AddSocks5ServerToolStripMenuItem.Text = "Add [Socks5] Server";
this.AddSocks5ServerToolStripMenuItem.Click += new System.EventHandler(this.AddSocks5ServerToolStripMenuItem_Click);
this.AddSocks5ServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
//
// AddShadowsocksServerToolStripMenuItem
//
this.AddShadowsocksServerToolStripMenuItem.Name = "AddShadowsocksServerToolStripMenuItem";
this.AddShadowsocksServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
this.AddShadowsocksServerToolStripMenuItem.Text = "Add [Shadowsocks] Server";
this.AddShadowsocksServerToolStripMenuItem.Click += new System.EventHandler(this.AddShadowsocksServerToolStripMenuItem_Click);
this.AddShadowsocksServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
//
// AddShadowsocksRServerToolStripMenuItem
//
this.AddShadowsocksRServerToolStripMenuItem.Name = "AddShadowsocksRServerToolStripMenuItem";
this.AddShadowsocksRServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
this.AddShadowsocksRServerToolStripMenuItem.Text = "Add [ShadowsocksR] Server";
this.AddShadowsocksRServerToolStripMenuItem.Click += new System.EventHandler(this.AddShadowsocksRServerToolStripMenuItem_Click);
this.AddShadowsocksRServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
//
// AddVMessServerToolStripMenuItem
//
this.AddVMessServerToolStripMenuItem.Name = "AddVMessServerToolStripMenuItem";
this.AddVMessServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
this.AddVMessServerToolStripMenuItem.Text = "Add [VMess] Server";
this.AddVMessServerToolStripMenuItem.Click += new System.EventHandler(this.AddVMessServerToolStripMenuItem_Click);
this.AddVMessServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
//
// AddTrojanServerToolStripMenuItem
//
this.AddTrojanServerToolStripMenuItem.Name = "AddTrojanServerToolStripMenuItem";
this.AddTrojanServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
this.AddTrojanServerToolStripMenuItem.Text = "Add [Trojan] Server";
this.AddTrojanServerToolStripMenuItem.Click += new System.EventHandler(this.AddTrojanServerToolStripMenuItem_Click);
this.AddTrojanServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
//
// ModeToolStripMenuItem
//
@@ -188,6 +193,15 @@ namespace Netch.Forms
this.ModeToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
this.ModeToolStripMenuItem.Text = "Mode";
//
// HelpToolStripMenuItem
//
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.CheckForUpdatesToolStripMenuItem});
this.HelpToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.HelpToolStripMenuItem.Name = "HelpToolStripMenuItem";
this.HelpToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
this.HelpToolStripMenuItem.Text = "Help";
//
// CreateProcessModeToolStripMenuItem
//
this.CreateProcessModeToolStripMenuItem.Name = "CreateProcessModeToolStripMenuItem";
@@ -282,6 +296,13 @@ namespace Netch.Forms
this.reinstallTapDriverToolStripMenuItem.Text = "Reinstall TUN/TAP driver";
this.reinstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.reinstallTapDriverToolStripMenuItem_Click);
//
// CheckForUpdatesToolStripMenuItem
//
this.CheckForUpdatesToolStripMenuItem.Name = "CheckForUpdatesToolStripMenuItem";
this.CheckForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
this.CheckForUpdatesToolStripMenuItem.Text = "Check for updates";
this.CheckForUpdatesToolStripMenuItem.Click += new System.EventHandler(this.CheckForUpdatesToolStripMenuItem_Click);
//
// AboutToolStripButton
//
this.AboutToolStripButton.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
@@ -292,6 +313,20 @@ namespace Netch.Forms
this.AboutToolStripButton.Text = "About";
this.AboutToolStripButton.Click += new System.EventHandler(this.AboutToolStripButton_Click);
//
// NewVersionLabel
//
this.NewVersionLabel.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.NewVersionLabel.BackColor = System.Drawing.Color.Transparent;
this.NewVersionLabel.ForeColor = System.Drawing.Color.Red;
this.NewVersionLabel.LinkColor = System.Drawing.Color.Red;
this.NewVersionLabel.IsLink = true;
this.NewVersionLabel.Visible = false;
this.NewVersionLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
this.NewVersionLabel.Name = "NewVersionLabel";
this.NewVersionLabel.Size = new System.Drawing.Size(26, 19);
this.NewVersionLabel.Text = "New version available";
this.NewVersionLabel.Click += new System.EventHandler(this.NewVersionLabel_Click);
//
// VersionLabel
//
this.VersionLabel.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
@@ -570,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
//
@@ -581,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
//
@@ -683,7 +720,6 @@ namespace Netch.Forms
this.Text = "Netch";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
this.Load += new System.EventHandler(this.MainForm_Load);
this.VisibleChanged += new System.EventHandler(this.MainForm_VisibleChanged);
this.MenuStrip.ResumeLayout(false);
this.MenuStrip.PerformLayout();
this.ConfigurationGroupBox.ResumeLayout(false);
@@ -732,6 +768,7 @@ namespace Netch.Forms
private System.Windows.Forms.SearchComboBox ModeComboBox;
private System.Windows.Forms.Label ModeLabel;
private System.Windows.Forms.ToolStripMenuItem ModeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem HelpToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel NatTypeStatusLabel;
private System.Windows.Forms.NotifyIcon NotifyIcon;
private System.Windows.Forms.ContextMenuStrip NotifyMenu;
@@ -742,6 +779,7 @@ namespace Netch.Forms
private System.Windows.Forms.TextBox ProfileNameText;
private System.Windows.Forms.TableLayoutPanel ProfileTable;
private System.Windows.Forms.ToolStripMenuItem reinstallTapDriverToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem CheckForUpdatesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ReloadModesToolStripMenuItem;
private System.Windows.Forms.ComboBox ServerComboBox;
private System.Windows.Forms.Label ServerLabel;
@@ -760,11 +798,11 @@ namespace Netch.Forms
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel UploadSpeedLabel;
private System.Windows.Forms.ToolStripStatusLabel UsedBandwidthLabel;
private System.Windows.Forms.ToolStripLabel NewVersionLabel;
private System.Windows.Forms.ToolStripLabel VersionLabel;
#endregion
private System.Windows.Forms.ToolStripStatusLabel NatTypeStatusLightLabel;
private System.Windows.Forms.ToolStripStatusLabel blankToolStripStatusLabel;
#endregion
}
}

View File

@@ -35,7 +35,10 @@ namespace Netch.Forms
if (result != null)
{
Global.Settings.Server.AddRange(result);
foreach (var server in result)
{
Global.Settings.Server.Add(server);
}
}
else
{
@@ -47,34 +50,23 @@ namespace Netch.Forms
}
}
private void AddSocks5ServerToolStripMenuItem_Click(object sender, EventArgs e)
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
{
new Socks5().Show();
Hide();
}
Form form = ((ToolStripMenuItem)sender).Name switch
{
"AddSocks5ServerToolStripMenuItem" => new Socks5(),
"AddShadowsocksServerToolStripMenuItem" => new Shadowsocks(),
"AddShadowsocksRServerToolStripMenuItem" => new ShadowsocksR(),
"AddVMessServerToolStripMenuItem" => new VMess(),
"AddTrojanServerToolStripMenuItem" => new Trojan(),
_ => null
};
private void AddShadowsocksServerToolStripMenuItem_Click(object sender, EventArgs e)
{
new Shadowsocks().Show();
Hide();
}
private void AddShadowsocksRServerToolStripMenuItem_Click(object sender, EventArgs e)
{
new ShadowsocksR().Show();
Hide();
}
private void AddVMessServerToolStripMenuItem_Click(object sender, EventArgs e)
{
new VMess().Show();
Hide();
}
private void AddTrojanServerToolStripMenuItem_Click(object sender, EventArgs e)
{
new Trojan().Show();
Hide();
form?.ShowDialog();
InitServer();
Configuration.Save();
Show();
}
#endregion
@@ -83,20 +75,18 @@ namespace Netch.Forms
private void CreateProcessModeToolStripButton_Click(object sender, EventArgs e)
{
new Process().Show();
Hide();
new Process().ShowDialog();
Show();
}
private async void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
try
{
await Task.Run(() =>
{
SaveConfigs();
InitMode();
});
Modes.Load();
InitMode();
NotifyTip(i18N.Translate("Modes have been reload"));
}
catch (Exception)
@@ -115,12 +105,20 @@ namespace Netch.Forms
private void ManageSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
new SubscribeForm().Show();
Hide();
new SubscribeForm().ShowDialog();
InitServer();
Show();
}
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;
@@ -152,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();
@@ -180,15 +178,19 @@ namespace Netch.Forms
lock (serverLock)
{
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
Global.Settings.Server.RemoveAll(server => server.Group == item.Remark);
var result = ShareLink.Parse(str);
if (result != null)
{
foreach (var x in result) x.Group = item.Remark;
Global.Settings.Server.AddRange(result);
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result.Count));
foreach (var server in result)
{
server.Group = item.Remark;
Global.Settings.Server.Add(server);
}
}
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result?.Count ?? 0));
}
}
catch (WebException e)
@@ -201,8 +203,8 @@ namespace Netch.Forms
}
})).ToArray());
InitServer();
Configuration.Save();
await Task.Run(InitServer);
StatusText(i18N.Translate("Subscription updated"));
}
catch (Exception)
@@ -213,7 +215,7 @@ namespace Netch.Forms
{
if (Global.Settings.UseProxyToUpdateSubscription)
{
await _mainController.Stop();
await MainController.Stop();
}
DisableItems(true);
@@ -224,6 +226,28 @@ namespace Netch.Forms
#region
private void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
void OnNewVersionNotFound(object o, EventArgs args)
{
_updater.NewVersionNotFound -= OnNewVersionNotFound;
NotifyTip(i18N.Translate("Already latest version"));
}
void OnNewVersionFoundFailed(object o, EventArgs args)
{
_updater.NewVersionFoundFailed -= OnNewVersionFoundFailed;
NotifyTip(i18N.Translate("New version found failed"), info: false);
}
_updater.NewVersionNotFound += OnNewVersionNotFound;
_updater.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
});
}
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
{
Utils.Utils.Open(".\\");
@@ -279,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);
@@ -298,7 +322,7 @@ namespace Netch.Forms
{
if (useProxy)
{
await _mainController.Stop();
await MainController.Stop();
State = State.Stopped;
}
@@ -365,8 +389,9 @@ namespace Netch.Forms
private void AboutToolStripButton_Click(object sender, EventArgs e)
{
new AboutForm().Show();
Hide();
new AboutForm().ShowDialog();
Show();
}
#endregion

View File

@@ -1,7 +1,14 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms.Mode;
using Netch.Utils;
using Process = System.Diagnostics.Process;
namespace Netch.Forms
{
@@ -18,11 +25,66 @@ namespace Netch.Forms
partial class MainForm
{
private readonly UpdateChecker _updater = new UpdateChecker();
private void CheckUpdate()
{
var updater = new UpdateChecker();
updater.NewVersionFound += (o, args) => { NotifyTip($"{i18N.Translate(@"New version available", ": ")}{updater.LatestVersionNumber}"); };
updater.Check(false, Global.Settings.CheckBetaUpdate);
_updater.NewVersionFound += (o, args) =>
{
if (_updater.LatestVersionDownloadUrl.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{_updater.LatestVersionNumber}");
NewVersionLabel.Visible = true;
}
};
_updater.Check(Global.Settings.CheckBetaUpdate);
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
var fileName = $"Netch{_updater.LatestVersionNumber}.zip";
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
var updateFileValid = false;
try
{
if (File.Exists(fileFullPath))
{
if (!(updateFileValid = Utils.Utils.IsZipValid(fileFullPath)))
{
File.Delete(fileFullPath);
}
}
if (!File.Exists(fileFullPath))
await WebUtil.DownloadFileAsync(WebUtil.CreateRequest(_updater.LatestVersionDownloadUrl), fileFullPath);
if (updateFileValid || Utils.Utils.IsZipValid(fileFullPath))
RunUpdater();
else
throw new InvalidDataException($"{fileFullPath} invalid");
}
catch (Exception exception)
{
NotifyTip($"{i18N.Translate("Download update failed")}\n{exception.Message}");
Logging.Error($"下载更新失败 {exception}");
}
void RunUpdater()
{
// if debugging process stopped, debugger will kill child processes!!!!
// 调试进程结束,调试器将会杀死子进程
// uncomment if(!Debugger.isAttach) block in NetchUpdater Project's main() method and attach to NetchUpdater process to debug
// 在 NetchUpdater 项目的 main() 方法中取消注释 if!Debugger.isAttach并附加到 NetchUpdater 进程进行调试
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Global.NetchDir, "NetchUpdater.exe"),
Arguments =
$"{Global.Settings.UDPSocketPort} {fileFullPath} {Global.NetchDir}"
});
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Utils;
@@ -15,6 +16,17 @@ namespace Netch.Forms
{
#region Server
private void InitServer()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ServerComboBox.Items.Clear();
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
SelectLastServer();
_comboBoxInitialized = comboBoxInitialized;
}
private static void TestServer()
{
try
@@ -28,11 +40,8 @@ namespace Netch.Forms
}
}
public void InitServer()
public void SelectLastServer()
{
ServerComboBox.Items.Clear();
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
// 如果值合法,选中该位置
if (Global.Settings.ServerComboBoxSelectedIndex > 0 &&
Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count)
@@ -52,95 +61,18 @@ namespace Netch.Forms
#region Mode
private void InitMode()
public void InitMode()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ModeComboBox.Items.Clear();
Global.ModeFiles.Clear();
if (Directory.Exists("mode"))
{
foreach (var name in Directory.GetFiles("mode", "*.txt"))
{
var ok = true;
var mode = new Models.Mode();
using (var sr = new StringReader(File.ReadAllText(name)))
{
var i = 0;
string text;
while ((text = sr.ReadLine()) != null)
{
if (i == 0)
{
var splited = text.Trim().Substring(1).Split(',');
if (splited.Length == 0)
{
ok = false;
break;
}
if (splited.Length >= 1)
{
mode.Remark = i18N.Translate(splited[0].Trim());
}
if (splited.Length >= 2)
{
if (int.TryParse(splited[1], out var result))
{
mode.Type = result;
}
else
{
ok = false;
break;
}
}
if (splited.Length >= 3)
{
if (int.TryParse(splited[2], out var result))
{
mode.BypassChina = result == 1;
}
else
{
ok = false;
break;
}
}
}
else
{
if (!text.StartsWith("#") && !string.IsNullOrWhiteSpace(text))
{
mode.Rule.Add(text.Trim());
}
}
i++;
}
}
if (ok)
{
mode.FileName = Path.GetFileNameWithoutExtension(name);
Global.ModeFiles.Add(mode);
}
}
var array = Global.ModeFiles.ToArray();
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
ModeComboBox.Items.AddRange(array);
SelectLastMode();
}
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
SelectLastMode();
_comboBoxInitialized = comboBoxInitialized;
}
private void SelectLastMode()
public void SelectLastMode()
{
// 如果值合法,选中该位置
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
@@ -157,29 +89,6 @@ namespace Netch.Forms
// 如果当前 ModeComboBox 中没元素,不做处理
}
public void AddMode(Models.Mode mode)
{
ModeComboBox.Items.Clear();
Global.ModeFiles.Add(mode);
var array = Global.ModeFiles.ToArray();
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
ModeComboBox.Items.AddRange(array);
SelectLastMode();
}
public void UpdateMode(Models.Mode NewMode, Models.Mode OldMode)
{
ModeComboBox.Items.Clear();
Global.ModeFiles.Remove(OldMode);
Global.ModeFiles.Add(NewMode);
var array = Global.ModeFiles.ToArray();
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
ModeComboBox.Items.AddRange(array);
SelectLastMode();
}
#endregion
/// <summary>

View File

@@ -1,6 +1,8 @@
using System;
using System.Drawing;
using System.Threading;
using System.Windows;
using Netch.Controllers;
using Netch.Models;
using Netch.Utils;
@@ -67,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;
@@ -87,6 +89,7 @@ namespace Netch.Forms
LastUploadBandwidth = 0;
LastDownloadBandwidth = 0;
Bandwidth.Stop();
ProfileGroupBox.Enabled = true;
StartDisableItems(true);
@@ -125,10 +128,17 @@ namespace Netch.Forms
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}", i18N.Translate(": "), text);
}
if (Enum.TryParse(text, false, out STUN_Client.NatType natType))
if (int.TryParse(text, out int natType))
{
NatTypeStatusLightLabel.Visible = true;
UpdateNatTypeLight(natType);
if (natType > 0 && natType < 5)
{
NatTypeStatusLightLabel.Visible = true;
UpdateNatTypeLight(natType);
}
}
else
{
NatTypeStatusLightLabel.Visible = false;
}
}
else
@@ -169,6 +179,35 @@ namespace Netch.Forms
NatTypeStatusLightLabel.ForeColor = c;
}
/// <summary>
/// 更新 NAT指示灯颜色
/// </summary>
/// <param name="natType"></param>
private void UpdateNatTypeLight(int natType)
{
Color c;
switch (natType)
{
case 1:
c = Color.LimeGreen;
break;
case 2:
c = Color.Yellow;
break;
case 3:
c = Color.Red;
break;
case 4:
c = Color.Black;
break;
default:
c = Color.Black;
break;
}
NatTypeStatusLightLabel.ForeColor = c;
}
/// <summary>
/// 更新状态栏文本
/// </summary>

View File

@@ -16,10 +16,6 @@ namespace Netch.Forms
{
public partial class MainForm : Form
{
/// <summary>
/// 主控制器
/// </summary>
private MainController _mainController = new MainController();
public MainForm()
{
@@ -31,35 +27,16 @@ namespace Netch.Forms
CheckForIllegalCrossThreadCalls = false;
}
private void SaveConfigs()
{
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
if (ModeComboBox.Items.Count != 0 && ModeComboBox.SelectedItem != null)
{
if (ModeComboBox.Tag is object[] list)
{
Global.Settings.ModeComboBoxSelectedIndex = list.ToList().IndexOf(ModeComboBox.SelectedItem);
}
else
{
Global.Settings.ModeComboBoxSelectedIndex = ModeComboBox.Items.IndexOf(ModeComboBox.SelectedItem);
}
}
Configuration.Save();
}
private void MainForm_Load(object sender, EventArgs e)
{
OnlyInstance.Called += OnCalled;
// 计算 ComboBox绘制 目标宽度
_eWidth = ServerComboBox.Width / 10;
// 加载服务器
InitServer();
// 加载模式
Modes.Load();
InitMode();
InitServer();
_comboBoxInitialized = true;
// 加载翻译
InitText();
@@ -106,6 +83,16 @@ namespace Netch.Forms
CheckUpdate();
}
});
Task.Run(() =>
{
// 检查订阅更新
if (Global.Settings.UpdateSubscribeatWhenOpened)
{
UpdateServersFromSubscribe();
}
});
}
private void OnCalled(object sender, OnlyInstance.Commands e)
@@ -149,7 +136,7 @@ namespace Netch.Forms
// 如果勾选了关闭时退出,自动点击退出按钮
else
{
Exit(true);
Exit();
}
}
}
@@ -162,15 +149,8 @@ namespace Netch.Forms
private void SettingsButton_Click(object sender, EventArgs e)
{
(Global.SettingForm = new SettingForm()).Show();
Hide();
}
private void MainForm_VisibleChanged(object sender, EventArgs e)
{
if (!Visible)
return;
new SettingForm().ShowDialog();
if (i18N.LangCode != Global.Settings.Language)
{
@@ -181,6 +161,8 @@ namespace Netch.Forms
if (ProfileButtons.Count != Global.Settings.ProfileCount)
InitProfile();
Show();
}
private void InitText()
@@ -193,6 +175,7 @@ namespace Netch.Forms
AddVMessServerToolStripMenuItem.Text = i18N.Translate("Add [VMess] Server");
AddTrojanServerToolStripMenuItem.Text = i18N.Translate("Add [Trojan] Server");
ModeToolStripMenuItem.Text = i18N.Translate("Mode");
HelpToolStripMenuItem.Text = i18N.Translate("Help");
CreateProcessModeToolStripMenuItem.Text = i18N.Translate("Create Process Mode");
SubscribeToolStripMenuItem.Text = i18N.Translate("Subscribe");
ManageSubscribeLinksToolStripMenuItem.Text = i18N.Translate("Manage Subscribe Links");
@@ -204,8 +187,10 @@ namespace Netch.Forms
UpdateACLToolStripMenuItem.Text = i18N.Translate("Update ACL");
updateACLWithProxyToolStripMenuItem.Text = i18N.Translate("Update ACL with proxy");
reinstallTapDriverToolStripMenuItem.Text = i18N.Translate("Reinstall TUN/TAP driver");
CheckForUpdatesToolStripMenuItem.Text = i18N.Translate("Check for updates");
OpenDirectoryToolStripMenuItem.Text = i18N.Translate("Open Directory");
AboutToolStripButton.Text = i18N.Translate("About");
NewVersionLabel.Text = i18N.Translate("New version available");
// VersionLabel.Text = i18N.Translate("xxx");
exitToolStripMenuItem.Text = i18N.Translate("Exit");
ConfigurationGroupBox.Text = i18N.Translate("Configuration");
@@ -234,9 +219,7 @@ namespace Netch.Forms
{
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
Visible = true;
ShowInTaskbar = true; // 显示在系统任务栏
WindowState = FormWindowState.Normal; // 还原窗体
NotifyIcon_MouseDoubleClick(null, null);
return;
}
@@ -248,14 +231,7 @@ namespace Netch.Forms
ControlFun();
}
for (var i = 0; i < 16; i++)
{
if (State == State.Waiting || State == State.Stopped)
break;
Thread.Sleep(250);
}
SaveConfigs();
Configuration.Save();
State = State.Terminating;
}
@@ -287,144 +263,130 @@ namespace Netch.Forms
private void EditServerPictureBox_Click(object sender, EventArgs e)
{
SaveConfigs();
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex != -1)
{
switch (Global.Settings.Server[ServerComboBox.SelectedIndex].Type)
{
case "Socks5":
new Socks5(ServerComboBox.SelectedIndex).Show();
break;
case "SS":
new Shadowsocks(ServerComboBox.SelectedIndex).Show();
break;
case "SSR":
new ShadowsocksR(ServerComboBox.SelectedIndex).Show();
break;
case "VMess":
new VMess(ServerComboBox.SelectedIndex).Show();
break;
case "Trojan":
new Trojan(ServerComboBox.SelectedIndex).Show();
break;
default:
return;
}
Hide();
}
else
if (ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Form server = Global.Settings.Server[ServerComboBox.SelectedIndex].Type switch
{
"Socks5" => new Socks5(Global.Settings.Server[ServerComboBox.SelectedIndex]),
"SS" => new Shadowsocks(Global.Settings.Server[ServerComboBox.SelectedIndex]),
"SSR" => new ShadowsocksR(Global.Settings.Server[ServerComboBox.SelectedIndex]),
"VMess" => new VMess(Global.Settings.Server[ServerComboBox.SelectedIndex]),
"Trojan" => new Trojan(Global.Settings.Server[ServerComboBox.SelectedIndex]),
_ => null
};
Hide();
server?.ShowDialog();
InitServer();
Configuration.Save();
Show();
}
private void SpeedPictureBox_Click(object sender, EventArgs e)
private async void SpeedPictureBox_Click(object sender, EventArgs e)
{
Enabled = false;
StatusText(i18N.Translate("Testing"));
Task.Run(() =>
try
{
await Task.Run(TestServer);
}
finally
{
TestServer();
Enabled = true;
StatusText(i18N.Translate("Test done"));
Refresh();
});
}
}
private void EditModePictureBox_Click(object sender, EventArgs e)
{
// 当前ModeComboBox中至少有一项
if (ModeComboBox.Items.Count > 0 && ModeComboBox.SelectedIndex != -1)
{
SaveConfigs();
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
// 只允许修改进程加速的模式
if (selectedMode.Type == 0)
{
//Process.Start(Environment.CurrentDirectory + "\\mode\\" + selectedMode.FileName + ".txt");
var process = new Process(selectedMode);
process.Text = "Edit Process Mode";
process.Show();
Hide();
}
}
else
if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
return;
}
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
switch (selectedMode.Type)
{
case 0:
{
Hide();
new Process(selectedMode).ShowDialog();
InitMode();
Show();
break;
}
default:
{
MessageBoxX.Show($"Current not support editing {selectedMode.TypeToString()} Mode");
break;
}
}
}
private void DeleteModePictureBox_Click(object sender, EventArgs e)
{
// 当前ModeComboBox中至少有一项
if (ModeComboBox.Items.Count > 0 && ModeComboBox.SelectedIndex != -1)
{
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
//删除模式文件
selectedMode.DeleteFile("mode");
ModeComboBox.Items.Clear();
Global.ModeFiles.Remove(selectedMode);
var array = Global.ModeFiles.ToArray();
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
ModeComboBox.Items.AddRange(array);
SelectLastMode();
Configuration.Save();
}
else
if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
return;
}
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
this.ModeComboBox.Items.Remove(selectedMode);
Modes.Delete(selectedMode);
SelectLastMode();
}
private void CopyLinkPictureBox_Click(object sender, EventArgs e)
{
// 当前ServerComboBox中至少有一项
if (ServerComboBox.SelectedIndex != -1)
{
var selectedMode = (Models.Server) ServerComboBox.SelectedItem;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
Clipboard.SetText(ShareLink.GetShareLink(selectedMode));
}
catch (Exception)
{
// ignored
}
}
else
if (ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
var selectedMode = (Models.Server) ServerComboBox.SelectedItem;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
Clipboard.SetText(ShareLink.GetShareLink(selectedMode));
}
catch (Exception)
{
// ignored
}
}
private void DeleteServerPictureBox_Click(object sender, EventArgs e)
{
// 当前 ServerComboBox 中至少有一项
if (ServerComboBox.SelectedIndex != -1)
{
var index = ServerComboBox.SelectedIndex;
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Models.Server);
ServerComboBox.Items.RemoveAt(index);
if (ServerComboBox.Items.Count > 0)
{
ServerComboBox.SelectedIndex = index != 0 ? index - 1 : index;
}
Configuration.Save();
}
else
if (ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
var index = ServerComboBox.SelectedIndex;
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Models.Server);
InitServer();
Configuration.Save();
if (ServerComboBox.Items.Count > 0)
{
ServerComboBox.SelectedIndex = index != 0 ? index - 1 : index;
}
}
@@ -472,14 +434,26 @@ namespace Netch.Forms
#endregion
private bool _comboBoxInitialized = false;
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
{
if (!_comboBoxInitialized) return;
Global.Settings.ModeComboBoxSelectedIndex = ModeComboBox.SelectedIndex;
}
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
{
if (!_comboBoxInitialized) return;
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
}
private void NatTypeStatusLabel_Click(object sender, EventArgs e)
{
if (_state == State.Started && MainController.NttTested)
{
MainController.NatTest();
}
}
}
}

View File

@@ -1,4 +1,7 @@
namespace Netch.Forms.Mode
using System;
using System.Windows.Forms;
namespace Netch.Forms.Mode
{
partial class Process
{
@@ -28,11 +31,10 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Process));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.UseCustomFilenameBox = new System.Windows.Forms.CheckBox();
this.TimeDataButton = new System.Windows.Forms.RadioButton();
this.StaySameButton = new System.Windows.Forms.RadioButton();
this.FilenameLabel = new System.Windows.Forms.Label();
this.FilenameTextBox = new System.Windows.Forms.TextBox();
this.ScanButton = new System.Windows.Forms.Button();
@@ -42,16 +44,17 @@
this.RuleListBox = new System.Windows.Forms.ListBox();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.ControlButton = new System.Windows.Forms.Button();
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ConfigurationGroupBox.SuspendLayout();
this.ProcessGroupBox.SuspendLayout();
this.contextMenuStrip.SuspendLayout();
this.SuspendLayout();
//
// ConfigurationGroupBox
//
this.ConfigurationGroupBox.Controls.Add(this.UseCustomFilenameBox);
this.ConfigurationGroupBox.Controls.Add(this.TimeDataButton);
this.ConfigurationGroupBox.Controls.Add(this.StaySameButton);
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
this.ConfigurationGroupBox.Controls.Add(this.ScanButton);
@@ -69,35 +72,13 @@
// UseCustomFilenameBox
//
this.UseCustomFilenameBox.AutoSize = true;
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 82);
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 76);
this.UseCustomFilenameBox.Name = "UseCustomFilenameBox";
this.UseCustomFilenameBox.Size = new System.Drawing.Size(152, 21);
this.UseCustomFilenameBox.TabIndex = 9;
this.UseCustomFilenameBox.Text = "Use Custom Filename";
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFileNameBox_CheckedChanged);
//
// TimeDataButton
//
this.TimeDataButton.AutoSize = true;
this.TimeDataButton.Location = new System.Drawing.Point(197, 106);
this.TimeDataButton.Name = "TimeDataButton";
this.TimeDataButton.Size = new System.Drawing.Size(84, 21);
this.TimeDataButton.TabIndex = 8;
this.TimeDataButton.TabStop = true;
this.TimeDataButton.Text = "Time data";
this.TimeDataButton.UseVisualStyleBackColor = true;
//
// StaySameButton
//
this.StaySameButton.AutoSize = true;
this.StaySameButton.Location = new System.Drawing.Point(84, 106);
this.StaySameButton.Name = "StaySameButton";
this.StaySameButton.Size = new System.Drawing.Size(107, 21);
this.StaySameButton.TabIndex = 7;
this.StaySameButton.TabStop = true;
this.StaySameButton.Text = "Stay the same";
this.StaySameButton.UseVisualStyleBackColor = true;
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFilenameBox_CheckedChanged);
//
// FilenameLabel
//
@@ -156,9 +137,9 @@
//
this.RuleListBox.FormattingEnabled = true;
this.RuleListBox.ItemHeight = 17;
this.RuleListBox.Location = new System.Drawing.Point(6, 134);
this.RuleListBox.Location = new System.Drawing.Point(6, 100);
this.RuleListBox.Name = "RuleListBox";
this.RuleListBox.Size = new System.Drawing.Size(328, 123);
this.RuleListBox.Size = new System.Drawing.Size(328, 157);
this.RuleListBox.TabIndex = 2;
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
//
@@ -168,6 +149,7 @@
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// RemarkLabel
//
@@ -178,6 +160,12 @@
this.RemarkLabel.TabIndex = 0;
this.RemarkLabel.Text = "Remark";
//
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(153, 48);
//
// ControlButton
//
this.ControlButton.Location = new System.Drawing.Point(277, 362);
@@ -188,6 +176,13 @@
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
//
// DeleteToolStripMenuItem
//
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.DeleteToolStripMenuItem.Text = "Delete";
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
//
// Process
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -195,27 +190,28 @@
this.ClientSize = new System.Drawing.Size(364, 397);
this.Controls.Add(this.ControlButton);
this.Controls.Add(this.ConfigurationGroupBox);
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 = "Process";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Create Process Mode";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ModeForm_FormClosing);
this.Load += new System.EventHandler(this.ModeForm_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();
this.ProcessGroupBox.ResumeLayout(false);
this.ProcessGroupBox.PerformLayout();
this.contextMenuStrip.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox ConfigurationGroupBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.GroupBox ProcessGroupBox;
private System.Windows.Forms.ListBox RuleListBox;
@@ -223,11 +219,9 @@
private System.Windows.Forms.TextBox ProcessNameTextBox;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.Button ScanButton;
private System.Windows.Forms.Button ControlButton;
public System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.Label FilenameLabel;
private System.Windows.Forms.TextBox FilenameTextBox;
private System.Windows.Forms.RadioButton StaySameButton;
private System.Windows.Forms.RadioButton TimeDataButton;
private System.Windows.Forms.CheckBox UseCustomFilenameBox;
}
}

View File

@@ -1,6 +1,9 @@
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;
@@ -9,11 +12,15 @@ namespace Netch.Forms.Mode
{
public partial class Process : Form
{
//用于判断当前窗口是否为编辑模式
private bool EditMode;
/// <summary>
/// 被编辑的模式
/// </summary>
private readonly Models.Mode _mode;
//被编辑模式坐标
private Models.Mode EditMode_Old;
/// <summary>
/// 是否被编辑过
/// </summary>
public bool Edited { get; private set; }
/// <summary>
/// 编辑模式
@@ -21,21 +28,25 @@ namespace Netch.Forms.Mode
/// <param name="mode">模式</param>
public Process(Models.Mode mode)
{
InitializeComponent();
if (mode.Type != 0)
{
throw new Exception("请传入进程模式");
}
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
EditMode_Old = mode;
Text = "Edit Process Mode";
//循环填充已有规则
mode.Rule.ForEach(i => RuleListBox.Items.Add(i));
this._mode = mode;
RuleListBox.Items.AddRange(mode.Rule.ToArray());
EditMode = true;
StaySameButton.Enabled = false;
TimeDataButton.Enabled = false;
FilenameTextBox.Enabled = false;
FilenameLabel.Enabled = false;
UseCustomFilenameBox.Enabled = false;
#region
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled =
UseCustomFilenameBox.Enabled = false;
#endregion
FilenameTextBox.Text = mode.FileName;
RemarkTextBox.Text = mode.Remark;
@@ -44,11 +55,9 @@ namespace Netch.Forms.Mode
public Process()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
EditMode = false;
EditMode_Old = null;
FilenameTextBox.Enabled = false;
}
/// <summary>
@@ -88,6 +97,7 @@ namespace Netch.Forms.Mode
if (FileChildInfo.Name.EndsWith(".exe") && !RuleListBox.Items.Contains(FileChildInfo.Name))
{
RuleListBox.Items.Add(FileChildInfo.Name);
Edited = true;
}
}
}
@@ -98,42 +108,17 @@ namespace Netch.Forms.Mode
}
}
private void ModeForm_Load(object sender, EventArgs e)
public void ModeForm_Load(object sender, EventArgs e)
{
Text = i18N.Translate(Text);
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
FilenameLabel.Text = i18N.Translate(FilenameLabel.Text);
UseCustomFilenameBox.Text = i18N.Translate(UseCustomFilenameBox.Text);
StaySameButton.Text = i18N.Translate(StaySameButton.Text);
TimeDataButton.Text = i18N.Translate(TimeDataButton.Text);
AddButton.Text = i18N.Translate(AddButton.Text);
ScanButton.Text = i18N.Translate(ScanButton.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
if (Global.Settings.ModeFileNameType == 0)
{
UseCustomFilenameBox.Checked = true;
StaySameButton.Enabled = false;
TimeDataButton.Enabled = false;
}
else if (Global.Settings.ModeFileNameType == 1)
{
FilenameTextBox.Enabled = false;
FilenameLabel.Enabled = false;
StaySameButton.Checked = true;
}
else
{
FilenameTextBox.Enabled = false;
FilenameLabel.Enabled = false;
TimeDataButton.Checked = true;
}
}
private void ModeForm_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
DeleteToolStripMenuItem.Text = i18N.Translate(DeleteToolStripMenuItem.Text);
}
/// <summary>
@@ -141,44 +126,48 @@ namespace Netch.Forms.Mode
/// </summary>
private void RuleListBox_MouseUp(object sender, MouseEventArgs e)
{
var strip = new ContextMenuStrip();
strip.Items.Add(i18N.Translate("Delete"));
RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y);
if (RuleListBox.SelectedIndex == -1)
return;
if (e.Button == MouseButtons.Right)
{
strip.Show(RuleListBox, e.Location); //鼠标右键按下弹出菜单
strip.MouseClick += deleteRule_Click;
contextMenuStrip.Show(RuleListBox, e.Location);
}
}
void deleteRule_Click(object sender, EventArgs e)
{
if (RuleListBox.SelectedIndex != -1)
{
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
}
if (RuleListBox.SelectedIndex == -1) return;
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
Edited = true;
}
private void AddButton_Click(object sender, EventArgs e)
private async void AddButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
await Task.Run(() =>
{
var process = ProcessNameTextBox.Text;
if (!process.EndsWith(".exe"))
if (!string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
{
process += ".exe";
}
var process = ProcessNameTextBox.Text;
if (!process.EndsWith(".exe"))
{
process += ".exe";
}
if (!RuleListBox.Items.Contains(process))
if (!RuleListBox.Items.Contains(process))
{
RuleListBox.Items.Add(process);
}
Edited = true;
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
ProcessNameTextBox.Text = string.Empty;
}
else
{
RuleListBox.Items.Add(process);
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
}
ProcessNameTextBox.Text = string.Empty;
}
else
{
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
}
});
}
private void ScanButton_Click(object sender, EventArgs e)
@@ -199,148 +188,83 @@ namespace Netch.Forms.Mode
}
}
private void ControlButton_Click(object sender, EventArgs e)
public void ControlButton_Click(object sender, EventArgs e)
{
if (EditMode)
if (RuleListBox.Items.Count == 0)
{
// 编辑模式
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
return;
}
if (RuleListBox.Items.Count != 0)
{
var mode = new Models.Mode
{
BypassChina = false,
FileName = FilenameTextBox.Text,
Type = 0,
Remark = RemarkTextBox.Text
};
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
return;
}
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";
}
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
return;
}
text = text.Substring(0, text.Length - 2);
if (_mode != null)
{
_mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear();
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
if (!Directory.Exists("mode"))
{
Directory.CreateDirectory("mode");
}
File.WriteAllText(Path.Combine("mode", FilenameTextBox.Text) + ".txt", text);
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
Global.MainForm.UpdateMode(mode, EditMode_Old);
Close();
}
else
{
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
}
Modes.WriteFile(_mode);
Edited = false;
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
// 自定义文件名
if (UseCustomFilenameBox.Checked)
var fullName = Modes.GetFullPath(FilenameTextBox.Text + ".txt");
if (File.Exists(fullName))
{
Global.Settings.ModeFileNameType = 0;
}
// 使用和备注一致的文件名
else if (StaySameButton.Checked)
{
Global.Settings.ModeFileNameType = 1;
FilenameTextBox.Text = RemarkTextBox.Text;
}
// 使用时间数据作为文件名
else
{
Global.Settings.ModeFileNameType = 2;
FilenameTextBox.Text = ((long) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds).ToString();
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
return;
}
Configuration.Save();
if (!string.IsNullOrWhiteSpace(RemarkTextBox.Text))
var mode = new Models.Mode
{
if (Global.Settings.ModeFileNameType == 0 && string.IsNullOrWhiteSpace(FilenameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
return;
}
BypassChina = false,
FileName = FilenameTextBox.Text,
Type = 0,
Remark = RemarkTextBox.Text
};
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
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"));
Global.MainForm.AddMode(mode);
Close();
}
else
{
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
}
}
else
{
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
}
Modes.WriteFile(mode);
Modes.Add(mode);
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private void UseCustomFileNameBox_CheckedChanged(object sender, EventArgs e)
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
{
if (UseCustomFilenameBox.Checked)
await Task.Run(() =>
{
StaySameButton.Enabled = false;
TimeDataButton.Enabled = false;
FilenameTextBox.Enabled = true;
FilenameLabel.Enabled = true;
}
else
{
StaySameButton.Enabled = true;
TimeDataButton.Enabled = true;
FilenameTextBox.Enabled = false;
FilenameLabel.Enabled = false;
}
if (!UseCustomFilenameBox.Checked)
{
var invalidFileChars = Path.GetInvalidFileNameChars();
var fileName = new StringBuilder(RemarkTextBox.Text);
foreach (var c in invalidFileChars)
{
fileName.Replace(c, '_');
}
FilenameTextBox.Text = fileName.ToString();
}
});
}
private void UseCustomFilenameBox_CheckedChanged(object sender, EventArgs e)
{
FilenameTextBox.Enabled = UseCustomFilenameBox.Checked;
}
}
}

View File

@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@@ -218,7 +218,6 @@
this.Name = "Shadowsocks";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Shadowsocks";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Shadowsocks_FormClosing);
this.Load += new System.EventHandler(this.Shadowsocks_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();

View File

@@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Utils;
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
{
public partial class Shadowsocks : Form
{
public int Index;
private readonly Models.Server _server;
/// <summary>
/// 初始化
/// </summary>
/// <param name="index">需要编辑的索引</param>
public Shadowsocks(int index = -1)
public Shadowsocks(Models.Server server = default)
{
InitializeComponent();
Index = index;
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SS[0]};
}
private void Shadowsocks_Load(object sender, EventArgs e)
{
#region InitText
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
@@ -32,44 +29,32 @@ namespace Netch.Forms.Server
PluginOptionsLabel.Text = i18N.Translate(PluginOptionsLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
foreach (var encrypt in Global.EncryptMethods.SS)
{
EncryptMethodComboBox.Items.Add(encrypt);
}
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SS.ToArray());
if (Index != -1)
{
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SS.IndexOf(Global.Settings.Server[Index].EncryptMethod);
PluginTextBox.Text = Global.Settings.Server[Index].Plugin;
PluginOptionsTextBox.Text = Global.Settings.Server[Index].PluginOption;
}
else
{
EncryptMethodComboBox.SelectedIndex = 0;
}
}
#endregion
private void Shadowsocks_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
RemarkTextBox.Text = _server.Remark;
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
PasswordTextBox.Text = _server.Password;
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SS.IndexOf(_server.EncryptMethod);
PluginTextBox.Text = _server.Plugin;
PluginOptionsTextBox.Text = _server.PluginOption;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
var cbx = sender as ComboBox;
if (cbx != null)
if (sender is ComboBox cbx)
{
e.DrawBackground();
if (e.Index >= 0)
{
var sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
var sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
var brush = new SolidBrush(cbx.ForeColor);
@@ -85,45 +70,28 @@ namespace Netch.Forms.Server
private void ControlButton_Click(object sender, EventArgs e)
{
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
if (!int.TryParse(PortTextBox.Text, out var port))
{
return;
}
if (Index == -1)
_server.Remark = RemarkTextBox.Text;
_server.Type = "SS";
_server.Hostname = AddressTextBox.Text;
_server.Port = port;
_server.Password = PasswordTextBox.Text;
_server.EncryptMethod = EncryptMethodComboBox.Text;
_server.Plugin = PluginTextBox.Text;
_server.PluginOption = PluginOptionsTextBox.Text;
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
{
Global.Settings.Server.Add(new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "SS",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text,
EncryptMethod = EncryptMethodComboBox.Text,
Plugin = PluginTextBox.Text,
PluginOption = PluginOptionsTextBox.Text
});
}
else
{
Global.Settings.Server[Index] = new Models.Server
{
Remark = RemarkTextBox.Text,
Group = Global.Settings.Server[Index].Group,
Type = "SS",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text,
EncryptMethod = EncryptMethodComboBox.Text,
Plugin = PluginTextBox.Text,
PluginOption = PluginOptionsTextBox.Text,
Country = null
};
Global.Settings.Server.Add(_server);
}
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));
Global.MainForm.InitServer();
Close();
}
}
}
}

View File

@@ -266,7 +266,6 @@
this.Name = "ShadowsocksR";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "ShadowsocksR";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ShadowsocksR_FormClosing);
this.Load += new System.EventHandler(this.ShadowsocksR_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();

View File

@@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Utils;
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
{
public partial class ShadowsocksR : Form
{
public int Index;
private readonly Models.Server _server;
/// <summary>
/// 初始化
/// </summary>
/// <param name="index">需要编辑的索引</param>
public ShadowsocksR(int index = -1)
public ShadowsocksR(Models.Server server = default)
{
InitializeComponent();
Index = index;
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SSR[0]};
}
private void ShadowsocksR_Load(object sender, EventArgs e)
{
#region InitText
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
@@ -34,58 +31,37 @@ namespace Netch.Forms.Server
OBFSParamLabel.Text = i18N.Translate(OBFSParamLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
foreach (var encrypt in Global.EncryptMethods.SSR)
{
EncryptMethodComboBox.Items.Add(encrypt);
}
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SSR.ToArray());
foreach (var protocol in Global.Protocols)
{
ProtocolComboBox.Items.Add(protocol);
}
ProtocolComboBox.Items.AddRange(Global.Protocols.ToArray());
OBFSComboBox.Items.AddRange(Global.OBFSs.ToArray());
foreach (var obfs in Global.OBFSs)
{
OBFSComboBox.Items.Add(obfs);
}
#endregion
if (Index != -1)
{
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SSR.IndexOf(Global.Settings.Server[Index].EncryptMethod);
ProtocolComboBox.SelectedIndex = Global.Protocols.IndexOf(Global.Settings.Server[Index].Protocol);
ProtocolParamTextBox.Text = Global.Settings.Server[Index].ProtocolParam;
OBFSComboBox.SelectedIndex = Global.OBFSs.IndexOf(Global.Settings.Server[Index].OBFS);
OBFSOptionParamTextBox.Text = Global.Settings.Server[Index].OBFSParam;
}
else
{
EncryptMethodComboBox.SelectedIndex = 0;
ProtocolComboBox.SelectedIndex = 0;
OBFSComboBox.SelectedIndex = 0;
}
}
private void ShadowsocksR_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
RemarkTextBox.Text = _server.Remark;
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
PasswordTextBox.Text = _server.Password;
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SSR.IndexOf(_server.EncryptMethod);
ProtocolComboBox.SelectedIndex = Global.Protocols.IndexOf(_server.Protocol);
ProtocolParamTextBox.Text = _server.ProtocolParam;
OBFSComboBox.SelectedIndex = Global.OBFSs.IndexOf(_server.OBFS);
OBFSOptionParamTextBox.Text = _server.OBFSParam;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
var cbx = sender as ComboBox;
if (cbx != null)
if (sender is ComboBox cbx)
{
e.DrawBackground();
if (e.Index >= 0)
{
var sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
var sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
var brush = new SolidBrush(cbx.ForeColor);
@@ -101,49 +77,30 @@ namespace Netch.Forms.Server
private void ControlButton_Click(object sender, EventArgs e)
{
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
if (!int.TryParse(PortTextBox.Text, out var port))
{
return;
}
if (Index == -1)
_server.Remark = RemarkTextBox.Text;
_server.Type = "SSR";
_server.Hostname = AddressTextBox.Text;
_server.Port = port;
_server.Password = PasswordTextBox.Text;
_server.EncryptMethod = EncryptMethodComboBox.Text;
_server.Protocol = ProtocolComboBox.Text;
_server.ProtocolParam = ProtocolParamTextBox.Text;
_server.OBFS = OBFSComboBox.Text;
_server.OBFSParam = OBFSOptionParamTextBox.Text;
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
{
Global.Settings.Server.Add(new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "SSR",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text,
EncryptMethod = EncryptMethodComboBox.Text,
Protocol = ProtocolComboBox.Text,
ProtocolParam = ProtocolParamTextBox.Text,
OBFS = OBFSComboBox.Text,
OBFSParam = OBFSOptionParamTextBox.Text
});
}
else
{
Global.Settings.Server[Index] = new Models.Server
{
Remark = RemarkTextBox.Text,
Group = Global.Settings.Server[Index].Group,
Type = "SSR",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text,
EncryptMethod = EncryptMethodComboBox.Text,
Protocol = ProtocolComboBox.Text,
ProtocolParam = ProtocolParamTextBox.Text,
OBFS = OBFSComboBox.Text,
OBFSParam = OBFSOptionParamTextBox.Text,
Country = null
};
Global.Settings.Server.Add(_server);
}
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));
Global.MainForm.InitServer();
Close();
}
}
}
}

View File

@@ -173,7 +173,6 @@
this.Name = "Socks5";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Socks5";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Shadowsocks_FormClosing);
this.Load += new System.EventHandler(this.Shadowsocks_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();

View File

@@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Utils;
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
{
public partial class Socks5 : Form
{
public int Index;
private readonly Models.Server _server;
/// <summary>
/// 初始化
/// </summary>
/// <param name="index">需要编辑的索引</param>
public Socks5(int index = -1)
public Socks5(Models.Server server = default)
{
InitializeComponent();
Index = index;
_server = server ?? new Models.Server();
}
private void Shadowsocks_Load(object sender, EventArgs e)
{
#region InitText
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
@@ -30,33 +27,28 @@ namespace Netch.Forms.Server
PasswordLabel.Text = i18N.Translate(PasswordLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
if (Index != -1)
{
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
UsernameTextBox.Text = Global.Settings.Server[Index].Username;
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
}
}
#endregion
private void Shadowsocks_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
RemarkTextBox.Text = _server.Remark;
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
UsernameTextBox.Text = _server.Username;
PasswordTextBox.Text = _server.Password;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
var cbx = sender as ComboBox;
if (cbx != null)
if (sender is ComboBox cbx)
{
e.DrawBackground();
if (e.Index >= 0)
{
var sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
var sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
var brush = new SolidBrush(cbx.ForeColor);
@@ -72,41 +64,26 @@ namespace Netch.Forms.Server
private void ControlButton_Click(object sender, EventArgs e)
{
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
if (!int.TryParse(PortTextBox.Text, out var port))
{
return;
}
if (Index == -1)
_server.Remark = RemarkTextBox.Text;
_server.Type = "Socks5";
_server.Hostname = AddressTextBox.Text;
_server.Port = port;
_server.Username = UsernameTextBox.Text;
_server.Password = PasswordTextBox.Text;
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
{
Global.Settings.Server.Add(new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "Socks5",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Username = UsernameTextBox.Text,
Password = PasswordTextBox.Text
});
}
else
{
Global.Settings.Server[Index] = new Models.Server
{
Remark = RemarkTextBox.Text,
Group = Global.Settings.Server[Index].Group,
Type = "Socks5",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Username = UsernameTextBox.Text,
Password = PasswordTextBox.Text,
Country = null
};
Global.Settings.Server.Add(_server);
}
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));
Global.MainForm.InitServer();
Close();
}
}
}
}

View File

@@ -152,7 +152,6 @@
this.Name = "Trojan";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Trojan";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Trojan_FormClosing);
this.Load += new System.EventHandler(this.Trojan_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();

View File

@@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Utils;
@@ -8,17 +7,13 @@ namespace Netch.Forms.Server
{
public partial class Trojan : Form
{
public int Index;
private readonly Models.Server _server;
/// <summary>
/// 初始化
/// </summary>
/// <param name="index">需要编辑的索引</param>
public Trojan(int index = -1)
public Trojan(Models.Server server = default)
{
InitializeComponent();
Index = index;
_server = server ?? new Models.Server();
}
private void Trojan_Load(object sender, EventArgs e)
@@ -29,32 +24,25 @@ namespace Netch.Forms.Server
PasswordLabel.Text = i18N.Translate(PasswordLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
if (Index != -1)
{
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
}
}
private void Trojan_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
RemarkTextBox.Text = _server.Remark;
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
PasswordTextBox.Text = _server.Password;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
var cbx = sender as ComboBox;
if (cbx != null)
if (sender is ComboBox cbx)
{
e.DrawBackground();
if (e.Index >= 0)
{
var sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
var sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
var brush = new SolidBrush(cbx.ForeColor);
@@ -70,39 +58,26 @@ namespace Netch.Forms.Server
private void ControlButton_Click(object sender, EventArgs e)
{
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
if (!int.TryParse(PortTextBox.Text, out var port))
{
return;
}
if (Index == -1)
_server.Remark = RemarkTextBox.Text;
_server.Type = "Trojan";
_server.Hostname = AddressTextBox.Text;
_server.Port = port;
_server.Password = PasswordTextBox.Text;
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
{
Global.Settings.Server.Add(new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "Trojan",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text
});
}
else
{
Global.Settings.Server[Index] = new Models.Server
{
Remark = RemarkTextBox.Text,
Group = Global.Settings.Server[Index].Group,
Type = "Trojan",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
Password = PasswordTextBox.Text,
Country = null
};
Global.Settings.Server.Add(_server);
}
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));
Global.MainForm.InitServer();
Close();
}
}
}
}

View File

@@ -385,7 +385,6 @@
this.Name = "VMess";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "VMess";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.VMess_FormClosing);
this.Load += new System.EventHandler(this.VMess_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();

View File

@@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Utils;
@@ -8,27 +7,28 @@ namespace Netch.Forms.Server
{
public partial class VMess : Form
{
public int Index;
private static Models.Server _server;
public VMess(int index = -1)
public VMess(Models.Server server = default)
{
InitializeComponent();
Index = index;
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.VMess[0]};
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
var cbx = sender as ComboBox;
if (cbx != null)
if (sender is ComboBox cbx)
{
e.DrawBackground();
if (e.Index >= 0)
{
var sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
var sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
var brush = new SolidBrush(cbx.ForeColor);
@@ -44,6 +44,8 @@ namespace Netch.Forms.Server
private void VMess_Load(object sender, EventArgs e)
{
#region InitText
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
@@ -60,116 +62,65 @@ namespace Netch.Forms.Server
UseMuxCheckBox.Text = i18N.Translate(UseMuxCheckBox.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
foreach (var encrypt in Global.EncryptMethods.VMess)
{
EncryptMethodComboBox.Items.Add(encrypt);
}
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.VMess.ToArray());
TransferProtocolComboBox.Items.AddRange(Global.TransferProtocols.ToArray());
FakeTypeComboBox.Items.AddRange(Global.FakeTypes.ToArray());
QUICSecurityComboBox.Items.AddRange(Global.EncryptMethods.VMessQUIC.ToArray());
foreach (var protocol in Global.TransferProtocols)
{
TransferProtocolComboBox.Items.Add(protocol);
}
#endregion
foreach (var fake in Global.FakeTypes)
{
FakeTypeComboBox.Items.Add(fake);
}
foreach (var security in Global.EncryptMethods.VMessQUIC)
{
QUICSecurityComboBox.Items.Add(security);
}
if (Index != -1)
{
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
UserIDTextBox.Text = Global.Settings.Server[Index].UserID;
AlterIDTextBox.Text = Global.Settings.Server[Index].AlterID.ToString();
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.VMess.IndexOf(Global.Settings.Server[Index].EncryptMethod);
TransferProtocolComboBox.SelectedIndex = Global.TransferProtocols.IndexOf(Global.Settings.Server[Index].TransferProtocol);
FakeTypeComboBox.SelectedIndex = Global.FakeTypes.IndexOf(Global.Settings.Server[Index].FakeType);
HostTextBox.Text = Global.Settings.Server[Index].Host;
PathTextBox.Text = Global.Settings.Server[Index].Path;
QUICSecurityComboBox.SelectedIndex = Global.EncryptMethods.VMessQUIC.IndexOf(Global.Settings.Server[Index].QUICSecure);
QUICSecretTextBox.Text = Global.Settings.Server[Index].QUICSecret;
TLSSecureCheckBox.Checked = Global.Settings.Server[Index].TLSSecure;
UseMuxCheckBox.Checked = Global.Settings.Server[Index].UseMux;
}
else
{
EncryptMethodComboBox.SelectedIndex = 0;
TransferProtocolComboBox.SelectedIndex = 0;
FakeTypeComboBox.SelectedIndex = 0;
QUICSecurityComboBox.SelectedIndex = 0;
}
}
private void VMess_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
RemarkTextBox.Text = _server.Remark;
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
UserIDTextBox.Text = _server.UserID;
AlterIDTextBox.Text = _server.AlterID.ToString();
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.VMess.IndexOf(_server.EncryptMethod);
TransferProtocolComboBox.SelectedIndex = Global.TransferProtocols.IndexOf(_server.TransferProtocol);
FakeTypeComboBox.SelectedIndex = Global.FakeTypes.IndexOf(_server.FakeType);
HostTextBox.Text = _server.Host;
PathTextBox.Text = _server.Path;
QUICSecurityComboBox.SelectedIndex = Global.EncryptMethods.VMessQUIC.IndexOf(_server.QUICSecure);
QUICSecretTextBox.Text = _server.QUICSecret;
TLSSecureCheckBox.Checked = _server.TLSSecure;
UseMuxCheckBox.Checked = _server.UseMux;
}
private void ControlButton_Click(object sender, EventArgs e)
{
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
if (!int.TryParse(PortTextBox.Text, out var port))
{
return;
}
if (AlterIDTextBox.Text == "")
{
MessageBoxX.Show(i18N.Translate("Please fill in alterID"));
return;
}
if (Index == -1)
{
Global.Settings.Server.Add(new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "VMess",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
UserID = UserIDTextBox.Text,
AlterID = int.Parse(AlterIDTextBox.Text),
EncryptMethod = EncryptMethodComboBox.Text,
TransferProtocol = TransferProtocolComboBox.Text,
FakeType = FakeTypeComboBox.Text,
Host = HostTextBox.Text,
Path = PathTextBox.Text,
QUICSecure = QUICSecurityComboBox.Text,
QUICSecret = QUICSecretTextBox.Text,
TLSSecure = TLSSecureCheckBox.Checked,
UseMux = UseMuxCheckBox.Checked
});
}
else
{
Global.Settings.Server[Index] = new Models.Server
{
Remark = RemarkTextBox.Text,
Type = "VMess",
Hostname = AddressTextBox.Text,
Port = int.Parse(PortTextBox.Text),
UserID = UserIDTextBox.Text,
AlterID = int.Parse(AlterIDTextBox.Text),
EncryptMethod = EncryptMethodComboBox.Text,
TransferProtocol = TransferProtocolComboBox.Text,
FakeType = FakeTypeComboBox.Text,
Host = HostTextBox.Text,
Path = PathTextBox.Text,
QUICSecure = QUICSecurityComboBox.Text,
QUICSecret = QUICSecretTextBox.Text,
TLSSecure = TLSSecureCheckBox.Checked,
UseMux = UseMuxCheckBox.Checked,
Country = null
};
}
Configuration.Save();
if (!int.TryParse(AlterIDTextBox.Text, out var alterId))
{
return;
}
_server.Remark = RemarkTextBox.Text;
_server.Type = "VMess";
_server.Hostname = AddressTextBox.Text;
_server.Port = port;
_server.UserID = UserIDTextBox.Text;
_server.AlterID = alterId;
_server.EncryptMethod = EncryptMethodComboBox.Text;
_server.TransferProtocol = TransferProtocolComboBox.Text;
_server.FakeType = FakeTypeComboBox.Text;
_server.Host = HostTextBox.Text;
_server.Path = PathTextBox.Text;
_server.QUICSecure = QUICSecurityComboBox.Text;
_server.QUICSecret = QUICSecretTextBox.Text;
_server.TLSSecure = TLSSecureCheckBox.Checked;
_server.UseMux = UseMuxCheckBox.Checked;
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
{
Global.Settings.Server.Add(_server);
}
MessageBoxX.Show(i18N.Translate("Saved"));
Global.MainForm.InitServer();
Close();
}
}
}
}

View File

@@ -61,11 +61,9 @@
this.DetectionIntervalLabel = new System.Windows.Forms.Label();
this.DetectionIntervalTextBox = new System.Windows.Forms.TextBox();
this.TcpingAtStartedCheckBox = new System.Windows.Forms.CheckBox();
this.STUN_ServerPortTextBox = new System.Windows.Forms.TextBox();
this.StunTextBoxSplitLabel = new System.Windows.Forms.Label();
this.STUNServerLabel = new System.Windows.Forms.Label();
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
this.STUN_ServerTextBox = new System.Windows.Forms.TextBox();
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();
@@ -74,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();
@@ -300,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);
@@ -309,11 +309,9 @@
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalLabel);
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalTextBox);
this.BehaviorGroupBox.Controls.Add(this.TcpingAtStartedCheckBox);
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerPortTextBox);
this.BehaviorGroupBox.Controls.Add(this.StunTextBoxSplitLabel);
this.BehaviorGroupBox.Controls.Add(this.STUNServerLabel);
this.BehaviorGroupBox.Controls.Add(this.RunAtStartupCheckBox);
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerTextBox);
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerComboBox);
this.BehaviorGroupBox.Controls.Add(this.MinimizeWhenStartedCheckBox);
this.BehaviorGroupBox.Controls.Add(this.ProfileCountLabel);
this.BehaviorGroupBox.Controls.Add(this.ProfileCountTextBox);
@@ -412,23 +410,6 @@
this.TcpingAtStartedCheckBox.Text = "Delay test after start";
this.TcpingAtStartedCheckBox.UseVisualStyleBackColor = true;
//
// STUN_ServerPortTextBox
//
this.STUN_ServerPortTextBox.Location = new System.Drawing.Point(366, 241);
this.STUN_ServerPortTextBox.Name = "STUN_ServerPortTextBox";
this.STUN_ServerPortTextBox.Size = new System.Drawing.Size(68, 23);
this.STUN_ServerPortTextBox.TabIndex = 8;
this.STUN_ServerPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// StunTextBoxSplitLabel
//
this.StunTextBoxSplitLabel.AutoSize = true;
this.StunTextBoxSplitLabel.Location = new System.Drawing.Point(353, 244);
this.StunTextBoxSplitLabel.Name = "StunTextBoxSplitLabel";
this.StunTextBoxSplitLabel.Size = new System.Drawing.Size(11, 17);
this.StunTextBoxSplitLabel.TabIndex = 12;
this.StunTextBoxSplitLabel.Text = ":";
//
// STUNServerLabel
//
this.STUNServerLabel.AutoSize = true;
@@ -448,13 +429,13 @@
this.RunAtStartupCheckBox.Text = "Run at startup";
this.RunAtStartupCheckBox.UseVisualStyleBackColor = true;
//
// STUN_ServerTextBox
// STUN_ServerComboBox
//
this.STUN_ServerTextBox.Location = new System.Drawing.Point(120, 241);
this.STUN_ServerTextBox.Name = "STUN_ServerTextBox";
this.STUN_ServerTextBox.Size = new System.Drawing.Size(233, 23);
this.STUN_ServerTextBox.TabIndex = 11;
this.STUN_ServerTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.STUN_ServerComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.STUN_ServerComboBox.Location = new System.Drawing.Point(120, 241);
this.STUN_ServerComboBox.Name = "STUN_ServerComboBox";
this.STUN_ServerComboBox.Size = new System.Drawing.Size(314, 25);
this.STUN_ServerComboBox.TabIndex = 11;
//
// MinimizeWhenStartedCheckBox
//
@@ -538,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);
@@ -556,7 +548,6 @@
this.Name = "SettingForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Settings";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SettingForm_FormClosing);
this.Load += new System.EventHandler(this.SettingForm_Load);
this.PortGroupBox.ResumeLayout(false);
this.PortGroupBox.PerformLayout();
@@ -565,7 +556,6 @@
this.BehaviorGroupBox.ResumeLayout(false);
this.BehaviorGroupBox.PerformLayout();
this.ResumeLayout(false);
}
#endregion
@@ -597,10 +587,8 @@
private System.Windows.Forms.TextBox ProfileCountTextBox;
private System.Windows.Forms.CheckBox MinimizeWhenStartedCheckBox;
private System.Windows.Forms.CheckBox RunAtStartupCheckBox;
private System.Windows.Forms.Label StunTextBoxSplitLabel;
private System.Windows.Forms.Label STUNServerLabel;
private System.Windows.Forms.TextBox STUN_ServerTextBox;
private System.Windows.Forms.TextBox STUN_ServerPortTextBox;
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;
@@ -615,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;
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using Netch.Models;
@@ -107,15 +109,15 @@ 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;
DetectionIntervalTextBox.Text = Global.Settings.StartedTcping_Interval.ToString();
STUN_ServerTextBox.Text = Global.Settings.STUN_Server;
STUN_ServerPortTextBox.Text = Global.Settings.STUN_Server_Port.ToString();
AclAddrTextBox.Text = Global.Settings.ACL;
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
LanguageComboBox.SelectedItem = Global.Settings.Language;
InitSTUN();
}
private void InitText()
@@ -141,32 +143,42 @@ 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);
DetectionIntervalLabel.Text = i18N.Translate(DetectionIntervalLabel.Text);
STUNServerLabel.Text = i18N.Translate(STUNServerLabel.Text);
StunTextBoxSplitLabel.Text = i18N.Translate(StunTextBoxSplitLabel.Text);
AclLabel.Text = i18N.Translate(AclLabel.Text);
LanguageLabel.Text = i18N.Translate(LanguageLabel.Text);
}
private void InitSTUN()
{
try
{
var stuns = File.ReadLines("bin\\stun.txt");
STUN_ServerComboBox.Items.AddRange(stuns.ToArray());
}
catch (Exception)
{
// ignored
}
STUN_ServerComboBox.Text = $"{Global.Settings.STUN_Server}:{Global.Settings.STUN_Server_Port}";
}
private void SettingForm_Load(object sender, EventArgs e)
{
InitText();
InitValue();
}
private void SettingForm_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
}
private void GlobalBypassIPsButton_Click(object sender, EventArgs e)
{
new GlobalBypassIPForm().Show();
Hide();
new GlobalBypassIPForm().ShowDialog();
Show();
}
private void ControlButton_Click(object sender, EventArgs e)
@@ -176,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;
@@ -185,8 +198,10 @@ namespace Netch.Forms
var scheduler = new TaskSchedulerClass();
scheduler.Connect();
var folder = scheduler.GetFolder("\\");
var taskIsExists = false;
try
{
folder.GetTask("Netch Startup");
taskIsExists = true;
@@ -215,8 +230,10 @@ 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
{
if (taskIsExists)
@@ -230,9 +247,7 @@ namespace Netch.Forms
return;
if (!CheckPortText("RedirectorTCP", ref RedirectorTextBox, ref Global.Settings.RedirectorTCPPort, PortType.TCP))
return;
Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1";
try
{
var Address = IPAddress.Parse(TUNTAPAddressTextBox.Text);
@@ -290,10 +305,13 @@ namespace Netch.Forms
try
{
var STUN_Server = STUN_ServerTextBox.Text;
var stun = STUN_ServerComboBox.Text.Split(':');
var STUN_Server = stun[0];
Global.Settings.STUN_Server = STUN_Server;
var STUN_ServerPort = int.Parse(STUN_ServerPortTextBox.Text);
var STUN_ServerPort = 3478;
if (stun.Length > 1)
STUN_ServerPort = int.Parse(stun[1]);
if (STUN_ServerPort > 0)
{
@@ -336,11 +354,9 @@ namespace Netch.Forms
}
Global.Settings.ACL = AclAddrTextBox.Text;
Global.Settings.TUNTAP.Address = TUNTAPAddressTextBox.Text;
Global.Settings.TUNTAP.Netmask = TUNTAPNetmaskTextBox.Text;
Global.Settings.TUNTAP.Gateway = TUNTAPGatewayTextBox.Text;
Global.Settings.TUNTAP.DNS.Clear();
foreach (var ip in TUNTAPDNSTextBox.Text.Split(','))
{
@@ -350,9 +366,7 @@ namespace Netch.Forms
Global.Settings.TUNTAP.UseCustomDNS = UseCustomDNSCheckBox.Checked;
Global.Settings.TUNTAP.ProxyDNS = ProxyDNSCheckBox.Checked;
Global.Settings.TUNTAP.UseFakeDNS = UseFakeDNSCheckBox.Checked;
Global.Settings.ModifySystemDNS = ModifySystemDNSCheckBox.Checked;
Configuration.Save();
MessageBoxX.Show(i18N.Translate("Saved"));
Close();

View File

@@ -32,37 +32,43 @@
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();
this.LinkLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.ControlButton = new System.Windows.Forms.Button();
this.SubscribeLinkListView = new System.Windows.Forms.ListView();
this.RemarkColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.LinkColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.UserAgentHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.RemarkColumnHeader = new System.Windows.Forms.ColumnHeader();
this.LinkColumnHeader = new System.Windows.Forms.ColumnHeader();
this.UserAgentHeader = new System.Windows.Forms.ColumnHeader();
this.pContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UseSelectedServerCheckBox = new System.Windows.Forms.CheckBox();
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
//
this.AddSubscriptionBox.Controls.Add(this.UserAgentTextBox);
this.AddSubscriptionBox.Controls.Add(this.ClearButton);
this.AddSubscriptionBox.Controls.Add(this.AddButton);
this.AddSubscriptionBox.Controls.Add(this.UserAgentLabel);
this.AddSubscriptionBox.Controls.Add(this.LinkTextBox);
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;
//
@@ -73,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);
@@ -98,6 +114,7 @@
this.LinkTextBox.Name = "LinkTextBox";
this.LinkTextBox.Size = new System.Drawing.Size(545, 23);
this.LinkTextBox.TabIndex = 4;
this.LinkTextBox.TextChanged += new System.EventHandler(this.ListTextBox_TextChanged);
//
// LinkLabel
//
@@ -124,33 +141,21 @@
this.RemarkLabel.TabIndex = 1;
this.RemarkLabel.Text = "Remark";
//
// ControlButton
//
this.ControlButton.Location = new System.Drawing.Point(597, 396);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 8;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
//
// SubscribeLinkListView
//
this.SubscribeLinkListView.AllowColumnReorder = true;
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.RemarkColumnHeader,
this.LinkColumnHeader,
this.UserAgentHeader});
this.SubscribeLinkListView.ContextMenuStrip = this.pContextMenuStrip;
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;
this.SubscribeLinkListView.SelectedIndexChanged += new System.EventHandler(this.SubscribeLinkListView_SelectedIndexChanged);
this.SubscribeLinkListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.SubscribeLinkListView_MouseUp);
//
// RemarkColumnHeader
//
@@ -169,9 +174,7 @@
//
// pContextMenuStrip
//
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.DeleteToolStripMenuItem,
this.CopyLinkToolStripMenuItem});
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem, this.CopyLinkToolStripMenuItem});
this.pContextMenuStrip.Name = "pContextMenuStrip";
this.pContextMenuStrip.Size = new System.Drawing.Size(130, 48);
//
@@ -192,25 +195,50 @@
// UseSelectedServerCheckBox
//
this.UseSelectedServerCheckBox.AutoSize = true;
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(12, 396);
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;
//
// MainTableLayoutPanel
//
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, 431);
this.Controls.Add(this.UseSelectedServerCheckBox);
this.Controls.Add(this.SubscribeLinkListView);
this.Controls.Add(this.ControlButton);
this.Controls.Add(this.AddSubscriptionBox);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.ClientSize = new System.Drawing.Size(684, 391);
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")));
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 = "SubscribeForm";
@@ -221,19 +249,21 @@
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();
}
#endregion
private System.Windows.Forms.Panel ControlsPanel;
private System.Windows.Forms.TableLayoutPanel MainTableLayoutPanel;
private System.Windows.Forms.Button ClearButton;
private System.Windows.Forms.GroupBox AddSubscriptionBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.TextBox LinkTextBox;
private System.Windows.Forms.Label LinkLabel;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.ListView SubscribeLinkListView;
private System.Windows.Forms.ColumnHeader RemarkColumnHeader;
private System.Windows.Forms.ColumnHeader LinkColumnHeader;
@@ -244,5 +274,7 @@
private System.Windows.Forms.TextBox UserAgentTextBox;
private System.Windows.Forms.ColumnHeader UserAgentHeader;
private System.Windows.Forms.CheckBox UseSelectedServerCheckBox;
#endregion
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Netch.Models;
using Netch.Utils;
@@ -8,6 +8,8 @@ namespace Netch.Forms
{
public partial class SubscribeForm : Form
{
private int _editingIndex = -1;
public SubscribeForm()
{
InitializeComponent();
@@ -19,20 +21,12 @@ namespace Netch.Forms
foreach (var item in Global.Settings.SubscribeLink)
{
if (!string.IsNullOrEmpty(item.UserAgent))
SubscribeLinkListView.Items.Add(new ListViewItem(new[]
{
SubscribeLinkListView.Items.Add(new ListViewItem(new[] {
item.Remark,
item.Link,
item.UserAgent}));
}
else
{
SubscribeLinkListView.Items.Add(new ListViewItem(new[] {
item.Remark,
item.Link,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"}));
}
!string.IsNullOrEmpty(item.UserAgent) ? item.UserAgent : WebUtil.DefaultUserAgent
}));
}
}
@@ -46,10 +40,10 @@ namespace Netch.Forms
CopyLinkToolStripMenuItem.Text = i18N.Translate(CopyLinkToolStripMenuItem.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
LinkLabel.Text = i18N.Translate(LinkLabel.Text);
ClearButton.Text = i18N.Translate(ClearButton.Text);
AddButton.Text = i18N.Translate(AddButton.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
UserAgentTextBox.Text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
ResetEditingGroup();
if (Global.Settings.Server.Count > 0)
{
@@ -67,8 +61,10 @@ namespace Netch.Forms
private void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
{
Global.MainForm.Show();
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
}
private void CopyLinkToolStripMenuItem_Click(object sender, EventArgs e)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
@@ -81,142 +77,208 @@ namespace Netch.Forms
}
}
}
private void DeleteToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBoxX.Show(i18N.Translate("Delete or not ? Will clean up the corresponding group of items in the server list"), confirm: true) == DialogResult.OK)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
{
DeleteSubscribe();
}
}
}
public void DeleteSubscribe()
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
{
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
{
var item = SubscribeLinkListView.SelectedItems[i];
var link = Global.Settings.SubscribeLink[item.Index];
var list = new List<Models.Server>();
foreach (var server in Global.Settings.Server)
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
{
if (server.Group != link.Remark)
{
list.Add(server);
}
var item = SubscribeLinkListView.SelectedItems[i];
DeleteServersInGroup(item.SubItems[0].Text);
Global.Settings.SubscribeLink.RemoveAt(item.Index);
SubscribeLinkListView.Items.Remove(item);
ResetEditingGroup();
}
Global.Settings.Server = list;
Global.Settings.SubscribeLink.RemoveAt(item.Index);
SubscribeLinkListView.Items.Remove(item);
Global.MainForm.InitServer();
}
}
}
private void AddButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(RemarkTextBox.Text))
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
{
if (!string.IsNullOrWhiteSpace(LinkTextBox.Text))
MessageBoxX.Show(i18N.Translate("Remark can not be empty"));
return;
}
if (string.IsNullOrWhiteSpace(LinkTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Link can not be empty"));
return;
}
if (!LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) && !LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase))
{
MessageBoxX.Show(i18N.Translate("Links must start with http:// or https://"));
return;
}
// 备注重复的订阅项
var duplicateRemarkItems = Global.Settings.SubscribeLink.Where(link => link.Remark.Equals(RemarkLabel.Text));
// 链接重复的订阅项
SubscribeLink duplicateLinkItem = null;
try
{
duplicateLinkItem = Global.Settings.SubscribeLink.First(link => link.Link.Equals(LinkTextBox.Text));
}
catch
{
// ignored
}
if (duplicateRemarkItems.Any())
{
MessageBoxX.Show("Remark Name Duplicate!");
return;
}
if (duplicateLinkItem != null)
{
if (duplicateLinkItem.Remark != RemarkTextBox.Text)
{
if (LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) || LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase))
{
//是否为新增订阅
var saveFlag = true;
Global.Settings.SubscribeLink.ForEach(subitem =>
{
if (subitem.Link.Equals(LinkTextBox.Text))
{
if (!subitem.Remark.Equals(RemarkTextBox.Text))
{
//修改了订阅备注,修改旧订阅服务器
Global.Settings.Server.ForEach(serverItem =>
{
try
{
//当前服务器组群组为订阅群组时批量修改备注
if (serverItem.Group == subitem.Remark) {
RenameServersGroup(duplicateLinkItem.Remark, RemarkTextBox.Text);
}
//serverItem.Group OldGroupRemark
//RemarkTextBox.Text NewGroupRemark
serverItem.Group = RemarkTextBox.Text;
}
}
catch (Exception)
{
}
});
subitem.Remark = RemarkTextBox.Text;
Global.MainForm.InitServer();
}
subitem.UserAgent = UserAgentTextBox.Text;
saveFlag = false;
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
MessageBoxX.Show(i18N.Translate("Saved"));
}
});
if (saveFlag)
{
Global.Settings.SubscribeLink.Add(new SubscribeLink
{
Remark = RemarkTextBox.Text,
Link = LinkTextBox.Text,
UserAgent = UserAgentTextBox.Text
});
}
RemarkTextBox.Text = string.Empty;
LinkTextBox.Text = string.Empty;
UserAgentTextBox.Text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
InitSubscribeLink();
}
else
{
MessageBoxX.Show(i18N.Translate("Links must start with http:// or https://"));
}
duplicateLinkItem.Remark = RemarkTextBox.Text;
duplicateLinkItem.UserAgent = UserAgentTextBox.Text;
}
else if (_editingIndex != -1)
{
// 只修改备注/未修改被上面处理
var target = Global.Settings.SubscribeLink[_editingIndex];
if (MessageBox.Show(i18N.Translate("Delete the corresponding group of items in the server list?"), i18N.Translate("Confirm"), MessageBoxButtons.YesNo) == DialogResult.Yes)
{
DeleteServersInGroup(target.Remark);
}
else
{
MessageBoxX.Show(i18N.Translate("Link can not be empty"));
RenameServersGroup(target.Remark, RemarkTextBox.Text);
}
target.Link = LinkTextBox.Text;
target.Remark = RemarkTextBox.Text;
target.UserAgent = UserAgentTextBox.Text;
}
else
{
MessageBoxX.Show(i18N.Translate("Remark can not be empty"));
Global.Settings.SubscribeLink.Add(new SubscribeLink
{
Remark = RemarkTextBox.Text,
Link = LinkTextBox.Text,
UserAgent = UserAgentTextBox.Text
});
}
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
// MessageBoxX.Show(i18N.Translate("Saved"));
ResetEditingGroup();
InitSubscribeLink();
}
private static void DeleteServersInGroup(string group)
{
Global.Settings.Server.RemoveAll(server => server.Group == group);
}
private static void RenameServersGroup(string oldGroup, string newGroup)
{
foreach (var server in Global.Settings.Server)
{
if (server.Group == oldGroup)
{
server.Group = newGroup;
}
}
}
private void ControlButton_Click(object sender, EventArgs e)
{
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
}
/// <summary>
/// 订阅列表选中节点
/// TODO 选中节点编辑
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
var editingCanOverwrite = true;
if (_editingIndex != -1)
{
RemarkTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[0].Text;
LinkTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[1].Text;
UserAgentTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[2].Text;
var targetItem = SubscribeLinkListView.Items[_editingIndex].SubItems;
editingCanOverwrite = RemarkTextBox.Text == targetItem[0].Text &&
LinkTextBox.Text == targetItem[1].Text &&
UserAgentTextBox.Text == targetItem[2].Text;
}
if (SubscribeLinkListView.SelectedItems.Count == 1)
{
if (editingCanOverwrite)
{
SelectEditing(SubscribeLinkListView.SelectedItems[0].Index);
}
}
else if (SubscribeLinkListView.SelectedItems.Count > 1)
{
}
else if (editingCanOverwrite)
{
// 不选
// 重置
ResetEditingGroup();
}
}
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
{
pContextMenuStrip.Show(SubscribeLinkListView, e.Location);
}
}
}
private void SelectEditing(int index)
{
_editingIndex = index;
ListViewItem target;
target = SubscribeLinkListView.Items[index];
AddSubscriptionBox.Text = target.SubItems[0].Text;
RemarkTextBox.Text = target.SubItems[0].Text;
LinkTextBox.Text = target.SubItems[1].Text;
UserAgentTextBox.Text = target.SubItems[2].Text;
}
private void ResetEditingGroup()
{
_editingIndex = -1;
AddSubscriptionBox.Text = string.Empty;
RemarkTextBox.Text = string.Empty;
LinkTextBox.Text = string.Empty;
UserAgentTextBox.Text = WebUtil.DefaultUserAgent;
}
private void ClearButton_Click(object sender, EventArgs e)
{
ResetEditingGroup();
}
private void ListTextBox_TextChanged(object sender, EventArgs e)
{
for (var i = 0; i < SubscribeLinkListView.Items.Count; i++)
{
if (((TextBox) sender).Text == SubscribeLinkListView.Items[i].SubItems[1].Text)
{
_editingIndex = i;
AddSubscriptionBox.Text = SubscribeLinkListView.Items[i].SubItems[0].Text;
}
}
}
}
}
}

View File

@@ -21,11 +21,6 @@ namespace Netch
/// </summary>
public static Forms.MainForm MainForm;
/// <summary>
/// 设置窗体
/// </summary>
public static Forms.SettingForm SettingForm;
/// <summary>
/// SS/SSR 加密方式
/// </summary>
@@ -206,8 +201,8 @@ namespace Netch
public static Models.Setting Settings = new Models.Setting();
/// <summary>
/// 用于存储模式文件内容
/// 用于存储模式
/// </summary>
public static List<Models.Mode> ModeFiles = new List<Models.Mode>();
public static readonly List<Models.Mode> Modes = new List<Models.Mode>();
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Text;
namespace Netch.Models.GitHubRelease
{
[Serializable]
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
{
public int Major { get; }
public int Minor { get; }
public int Patch { get; }
public string PreRelease { get; }
public int Build { get; }
public SuffixVersion(int major, int minor, int patch, string preRelease, int build)
{
Major = major;
Minor = minor;
Patch = patch;
PreRelease = preRelease;
Build = build;
}
public SuffixVersion(Version version, string preRelease, int build)
{
Major = version.Major;
Minor = version.Minor;
Patch = version.Build;
PreRelease = preRelease;
Build = build;
}
public static SuffixVersion Parse(string input)
{
var splitStr = input.Split('-');
var dotNetVersion = Version.Parse(splitStr[0]);
var preRelease = new StringBuilder();
var build = 0;
if (splitStr.Length > 1)
foreach (var c in splitStr[1])
{
if (int.TryParse(c.ToString(), out var n))
{
build = build * 10 + n;
}
else
{
preRelease.Append(c);
}
}
return new SuffixVersion(dotNetVersion, preRelease.ToString(), build);
}
public static bool TryParse(string input, out SuffixVersion result)
{
try
{
result = Parse(input);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
public object Clone() => new SuffixVersion(Major, Major, Patch, PreRelease, Build);
public int CompareTo(object obj)
{
if (obj is SuffixVersion version)
return CompareTo(version);
return -1;
}
/// <summary>
///
/// </summary>
/// <param name="other"></param>
/// <returns>
/// greater than 0 newer
/// </returns>
public int CompareTo(SuffixVersion other)
{
var majorComparison = Major.CompareTo(other.Major);
if (majorComparison != 0)
return majorComparison;
var minorComparison = Minor.CompareTo(other.Minor);
if (minorComparison != 0)
return minorComparison;
var patchComparison = Patch.CompareTo(other.Patch);
if (patchComparison != 0)
return patchComparison;
if (PreRelease == string.Empty)
return other.PreRelease == string.Empty ? 0 : 1;
if (other.PreRelease == string.Empty)
return -1;
var suffixComparison = string.Compare(PreRelease, other.PreRelease, StringComparison.Ordinal);
if (suffixComparison != 0)
return suffixComparison;
return Build.CompareTo(other.Build);
}
public bool Equals(SuffixVersion other)
{
return Major == other.Major && Minor == other.Minor && Patch == other.Patch && PreRelease == other.PreRelease && Build == other.Build;
}
public override bool Equals(object obj)
{
return obj is SuffixVersion other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Major;
hashCode = (hashCode * 397) ^ Minor;
hashCode = (hashCode * 397) ^ Patch;
hashCode = (hashCode * 397) ^ (PreRelease != null ? PreRelease.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Build;
return hashCode;
}
}
public override string ToString()
{
return $"{Major}.{Minor}.{Patch}{(string.IsNullOrEmpty(PreRelease) ? "" : "-")}{PreRelease}{(Build == 0 ? "" : Build.ToString())}";
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace Netch.Models.GitHubRelease
@@ -20,9 +19,7 @@ namespace Netch.Models.GitHubRelease
private static bool IsVersionString(string str)
{
if (Global.Settings.CheckBetaUpdate)
str = str.Split('-')[0];
return Version.TryParse(str, out _);
return SuffixVersion.TryParse(str, out _);
}
/// <returns> =0:versions are equal</returns>
@@ -30,28 +27,9 @@ namespace Netch.Models.GitHubRelease
/// <returns> &lt;0:version2 is greater</returns>
public static int CompareVersion(string v1, string v2)
{
var version1 = ToVersion(v1);
var version2 = ToVersion(v2);
var res = version1.CompareTo(version2);
return res;
}
private static Version ToVersion(string versionStr)
{
var v = versionStr.Split('-');
var version = Version.Parse(v[0]);
if (v.Length == 1)
return version;
var beta = v[1];
var result = string.Empty;
foreach (var c in beta)
{
if (int.TryParse(c.ToString(), out var n))
result += n;
}
return new Version(version.Major, version.Minor, version.Build, int.Parse(result));
var version1 = SuffixVersion.Parse(v1);
var version2 = SuffixVersion.Parse(v2);
return version1.CompareTo(version2);
}
}
}

View File

@@ -1,5 +1,8 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Netch.Utils;
namespace Netch.Models
{
@@ -10,6 +13,11 @@ namespace Netch.Models
/// </summary>
public string Remark;
/// <summary>
/// 文件相对路径(必须是存在的文件)
/// </summary>
public string RelativePath;
/// <summary>
/// 无后缀文件名
/// </summary>
@@ -20,9 +28,9 @@ namespace Netch.Models
/// 0. Socks5 + 进程加速<para />
/// 1. Socks5 + TUN/TAP 规则内 IP CIDR 加速<para />
/// 2. Socks5 + TUN/TAP 全局,绕过规则内 IP CIDR<para />
/// 3. Socks5 + HTTP 代理(自动设置到系统代理)<para />
/// 4. Socks5 代理(不自动设置到系统代理)<para />
/// 5. Socks5 + HTTP 代理(不自动设置到系统代理)<para />
/// 3. Socks5 + HTTP 代理(设置到系统代理)<para />
/// 4. Socks5 代理(不设置到系统代理)<para />
/// 5. Socks5 + HTTP 代理(不设置到系统代理)<para />
/// </summary>
public int Type = 0;
@@ -34,7 +42,7 @@ namespace Netch.Models
/// <summary>
/// 规则
/// </summary>
public List<string> Rule = new List<string>();
public readonly List<string> Rule = new List<string>();
/// <summary>
/// 获取备注
@@ -42,7 +50,7 @@ namespace Netch.Models
/// <returns>备注</returns>
public override string ToString()
{
return string.Format("[{0}] {1}", Type + 1, Remark);
return $"[{Type + 1}] {Remark}";
}
/// <summary>
@@ -51,85 +59,44 @@ namespace Netch.Models
/// <returns>模式文件字符串</returns>
public string ToFileString()
{
string FileString;
string fileString;
// 进程模式
if (Type == 0)
switch (Type)
{
FileString = $"# {Remark}\r\n";
case 0:
// 进程模式
fileString = $"# {Remark}";
break;
case 1:
// TUN/TAP 规则内 IP CIDR无 Bypass China 设置
fileString = $"# {Remark}, {Type}, 0";
break;
default:
fileString = $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}";
break;
}
// TUN/TAP 规则内 IP CIDR无 Bypass China 设置
else if (Type == 1)
{
FileString = $"# {Remark}, {Type}, 0\r\n";
}
fileString += Global.EOF;
// TUN/TAP 全局,绕过规则内 IP CIDR
// HTTP 代理(自动设置到系统代理)
// Socks5 代理(不自动设置到系统代理)
// Socks5 + HTTP 代理(不自动设置到系统代理)
else
{
FileString = $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}\r\n";
}
fileString = Rule.Aggregate(fileString, (current, item) => $"{current}{item}{Global.EOF}");
// 去除最后的行尾符
fileString = fileString.Substring(0, fileString.Length - 2);
foreach (var item in Rule)
{
FileString = $"{FileString}{item}\r\n";
}
// 去除最后两个多余回车符和换行符
FileString = FileString.Substring(0, FileString.Length - 2);
return FileString;
return fileString;
}
/// <summary>
/// 写入模式文件
/// </summary>
public void ToFile(string Dir)
public string TypeToString()
{
if (!System.IO.Directory.Exists(Dir))
return Type switch
{
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(string Dir)
{
if (System.IO.Directory.Exists(Dir))
{
var NewPath = System.IO.Path.Combine(Dir, FileName);
if (System.IO.File.Exists(NewPath + ".txt"))
{
System.IO.File.Delete(NewPath + ".txt");
}
}
0 => "Process",
1 => "TUNTAP",
2 => "TUNTAP",
3 => "SYSTEM",
4 => "S5",
5 => "S5+HTTP",
_ => "ERROR",
};
}
}
}
}

View File

@@ -76,7 +76,7 @@ namespace Netch.Models
/// <summary>
/// 协议SSR
/// </summary>
public string Protocol;
public string Protocol = Global.Protocols[0];
/// <summary>
/// 协议参数SSR
@@ -86,7 +86,7 @@ namespace Netch.Models
/// <summary>
/// 混淆SSR
/// </summary>
public string OBFS;
public string OBFS = Global.OBFSs[0];
/// <summary>
/// 混淆参数SSR
@@ -96,12 +96,12 @@ namespace Netch.Models
/// <summary>
/// 传输协议VMess
/// </summary>
public string TransferProtocol = "tcp";
public string TransferProtocol = Global.TransferProtocols[0];
/// <summary>
/// 伪装类型VMess
/// </summary>
public string FakeType = string.Empty;
public string FakeType = Global.FakeTypes[0];
/// <summary>
/// 伪装域名VMessHTTP、WebSocket、HTTP/2
@@ -116,7 +116,7 @@ namespace Netch.Models
/// <summary>
/// QUIC 加密方式VMess
/// </summary>
public string QUICSecure = "none";
public string QUICSecure = Global.EncryptMethods.VMessQUIC[0];
/// <summary>
/// QUIC 加密密钥VMess
@@ -131,7 +131,7 @@ namespace Netch.Models
/// <summary>
/// Mux 多路复用VMess
/// </summary>
public bool UseMux = false;
public bool UseMux = true;
/// <summary>
/// 延迟
@@ -144,10 +144,10 @@ namespace Netch.Models
public string Country;
/// <summary>
/// 获取备注
/// </summary>
/// <returns>备注</returns>
public override string ToString()
/// 获取备注
/// </summary>
/// <returns>备注</returns>
public override string ToString()
{
if (string.IsNullOrWhiteSpace(Remark))
{
@@ -220,4 +220,4 @@ namespace Netch.Models
}
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace Netch.Models
{
@@ -87,12 +88,17 @@ namespace Netch.Models
/// 是否打开软件时检查更新
/// </summary>
public bool CheckUpdateWhenOpened = true;
/// <summary>
/// 是否检查 Beta 更新
/// </summary>
public bool CheckBetaUpdate = false;
/// <summary>
/// 是否打开软件时更新订阅
/// </summary>
public bool UpdateSubscribeatWhenOpened = false;
/// <summary>
/// 修改系统 DNS
/// </summary>
@@ -103,12 +109,6 @@ namespace Netch.Models
/// </summary>
public int RequestTimeout = 10000;
/// <summary>
/// 使用何种模式文件名
/// 0 为自定义文件名1 为使用和备注一致的文件名2 为使用时间数据作为文件名
/// </summary>
public int ModeFileNameType = 1;
/// <summary>
/// HTTP 本地端口
/// </summary>
@@ -152,7 +152,7 @@ namespace Netch.Models
/// <summary>
/// 服务器列表
/// </summary>
public List<Server> Server = new List<Server>();
public readonly List<Server> Server = new List<Server>();
/// <summary>
/// 全局绕过 IP 列表
@@ -172,7 +172,7 @@ namespace Netch.Models
/// <summary>
/// STUN测试服务器
/// </summary>
public string STUN_Server = "stun.stunprotocol.org";
public string STUN_Server = "stun.syncthing.net";
/// <summary>
/// STUN测试服务器
@@ -204,4 +204,4 @@ namespace Netch.Models
/// </summary>
public string Language = "System";
}
}
}

View File

@@ -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,39 +65,9 @@ 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);
// 退出进程
Environment.Exit(1);
}
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}"); });
Logging.Info("启动单实例");
Task.Run(OnlyInstance.Server);
// 绑定错误捕获

View File

@@ -57,20 +57,23 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ini-parser" Version="2.5.2" />
<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.56" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.58" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<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>
<ProjectReference Include="..\NetchLib\NetchLib.csproj" />
<ProjectReference Include="..\NetchUpdater\NetchUpdater.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.IO.Compression" />
<Reference Include="System.Management" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ServiceProcess" />
@@ -108,6 +111,6 @@
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="@ECHO OFF&#xD;&#xA;RD /S /Q $(TargetDir)bin &gt; NUL 2&gt;&amp;1&#xD;&#xA;RD /S /Q $(TargetDir)i18n &gt; NUL 2&gt;&amp;1&#xD;&#xA;RD /S /Q $(TargetDir)mode &gt; NUL 2&gt;&amp;1&#xD;&#xA;&#xD;&#xA;MKDIR $(TargetDir)bin &gt; NUL 2&gt;&amp;1&#xD;&#xA;MKDIR $(TargetDir)i18n &gt; NUL 2&gt;&amp;1&#xD;&#xA;MKDIR $(TargetDir)mode &gt; NUL 2&gt;&amp;1&#xD;&#xA;&#xD;&#xA;COPY /Y $(SolutionDir)binaries\* $(TargetDir)bin &gt; NUL 2&gt;&amp;1&#xD;&#xA;COPY /Y $(SolutionDir)translations\i18n\* $(TargetDir)i18n &gt; NUL 2&gt;&amp;1&#xD;&#xA;COPY /Y $(SolutionDir)modes\mode\* $(TargetDir)mode &gt; NUL 2&gt;&amp;1&#xD;&#xA;MKDIR $(TargetDir)bin\tap-driver &gt; NUL 2&gt;&amp;1&#xD;&#xA;COPY /Y $(SolutionDir)binaries\tap-driver\* $(TargetDir)bin\tap-driver &gt; NUL 2&gt;&amp;1&#xD;&#xA;&#xD;&#xA;DEL / f $(TargetDir)\*.config&#xD;&#xA;DEL / f $(TargetDir)\*.pdb&#xD;&#xA;RD /s /Q $(TargetDir)\x86" />
<Exec Command="set Configuration=$(Configuration)&#xD;&#xA;set ILMergeConsolePath=$(ILMergeConsolePath)&#xD;&#xA;set TargetDir=$(TargetDir)&#xD;&#xA;set SolutionDir=$(SolutionDir)&#xD;&#xA;$(ProjectDir)PostBuild.bat" />
</Target>
</Project>

40
Netch/PostBuild.bat Normal file
View 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

View File

@@ -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 {

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,14 @@
"Add [Trojan] Server": "添加 [Trojan] 服务器",
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口。",
"New version available": "发现新版本",
"Already latest version": "已经是最新版本",
"New version found failed": "寻找新版本失败",
"Mode": "模式",
"Help": "帮助",
"Check for updates": "检查更新",
"Download and install now?": "立即下载并安装?",
"Start downloading new version": "开始下载新版本",
"Download update failed": "下载更新错误",
"Create Process Mode": "创建进程模式",
"Edit Process Mode": "修改进程模式",
@@ -158,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": "快捷配置数量",

View File

@@ -6,116 +6,131 @@ 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>
/// 计算流量
/// </summary>
/// <param name="bandwidth">流量</param>
/// <returns>带单位的流量字符串</returns>
public static string Compute(long bandwidth)
public static string Compute(ulong size)
{
string[] units = {"KB", "MB", "GB", "TB", "PB"};
double result = bandwidth;
var i = -1;
do
var mStrSize = @"0";
const double step = 1024.00;
var factSize = size;
if (factSize < step)
{
i++;
} while ((result /= 1024) > 1024);
if (result < 0)
mStrSize = $@"{factSize:0.##} B";
}
else if (factSize >= step && factSize < 1048576)
{
result = 0;
mStrSize = $@"{factSize / step:0.##} KB";
}
else if (factSize >= 1048576 && factSize < 1073741824)
{
mStrSize = $@"{factSize / step / step:0.##} MB";
}
else if (factSize >= 1073741824 && factSize < 1099511627776)
{
mStrSize = $@"{factSize / step / step / step:0.##} GB";
}
else if (factSize >= 1099511627776)
{
mStrSize = $@"{factSize / step / step / step / step:0.##} TB";
}
return string.Format("{0} {1}", Math.Round(result, 2), units[i]);
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)
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();
Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})"+instance.ProcessName).ToArray()));
Logging.Info("流量统计进程:" + string.Join(",",
instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray()));
Task.Run(() =>
{
using (var session = new TraceEventSession("KernelAndClrEventsSession"))
tSession = new TraceEventSession("KernelAndClrEventsSession");
tSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
//这玩意儿上传和下载得到的data是一样的:)
//所以暂时没办法区分上传下载流量
tSession.Source.Kernel.TcpIpRecv += data =>
{
session.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
//这玩意儿上传和下载得到的data是一样的:)
//所以暂时没办法区分上传下载流量
session.Source.Kernel.TcpIpRecv += data =>
if (processList.Contains(data.ProcessID))
{
if (processList.Contains(data.ProcessID))
{
lock (counterLock)
received += data.size;
//Logging.Info($"TcpIpRecv: {Compute(data.size)}");
}
};
session.Source.Kernel.UdpIpRecv += data =>
{
if (processList.Contains(data.ProcessID))
{
lock (counterLock)
received += data.size;
//Logging.Info($"UdpIpRecv: {Compute(data.size)}");
}
};
lock (counterLock)
received += (ulong) data.size;
session.Source.Process();
}
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
}
};
tSession.Source.Kernel.UdpIpRecv += data =>
{
if (processList.Contains(data.ProcessID))
{
lock (counterLock)
received += (ulong) data.size;
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
}
};
tSession.Source.Process();
});
if ((Convert.ToInt32(Global.MainForm.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
{
Global.MainForm.OnBandwidthUpdated(0);
received = 0;
}
while (Global.MainForm.State != State.Stopped)
{
Task.Delay(1000).Wait();
lock (counterLock)
{
Global.MainForm.OnBandwidthUpdated(Convert.ToInt64(received));
Global.MainForm.OnBandwidthUpdated(received);
}
}
}
public static void Stop()
{
tSession?.Dispose();
received = 0;
}
}
}

View File

@@ -16,7 +16,7 @@ namespace Netch.Utils
/// <summary>
/// 数据目录
/// </summary>
public static readonly string DATA_DIR = "data";
public const string DATA_DIR = "data";
/// <summary>
/// 设置

138
Netch/Utils/Modes.cs Normal file
View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Netch.Models;
namespace Netch.Utils
{
public static class Modes
{
private const string MODE_DIR = "mode";
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>
public static void Load()
{
Global.Modes.Clear();
if (!Directory.Exists(MODE_DIR)) return;
var stack = new Stack<string>();
stack.Push(MODE_DIR);
while (stack.Count > 0)
{
var dirInfo = new DirectoryInfo(stack.Pop());
try
{
foreach (var childDirInfo in dirInfo.GetDirectories())
stack.Push(childDirInfo.FullName);
foreach (var childFileInfo in dirInfo.GetFiles().Where(info => info.Name.EndsWith(".txt")))
LoadModeFile(childFileInfo.FullName);
}
catch
{
// ignored
}
}
Sort();
}
private static void LoadModeFile(string fullName)
{
var mode = new Mode
{
FileName = Path.GetFileNameWithoutExtension(fullName),
RelativePath = GetRelativePath(fullName)
};
var content = File.ReadAllLines(fullName);
if (content.Length == 0) return;
for (var i = 0; i < content.Length; i++)
{
var text = content[i];
if (i == 0)
{
var splited = text.Substring(text.IndexOf('#') + 1).Split(',').Select(s => s.Trim()).ToArray();
try
{
string tmp;
if ((tmp = splited.ElementAtOrDefault(0)) != null)
mode.Remark = i18N.Translate(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;
}
catch
{
return;
}
}
else
{
if (!text.StartsWith("#") && !string.IsNullOrWhiteSpace(text))
mode.Rule.Add(text.Trim());
}
}
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));
}
public static void Add(Mode mode)
{
Global.Modes.Add(mode);
Sort();
Global.MainForm.InitMode();
}
public static void Delete(Mode mode)
{
var fullName = GetFullPath(mode);
if (File.Exists(fullName))
{
File.Delete(fullName);
}
Global.Modes.Remove(mode);
Global.MainForm.InitMode();
}
}
}

View File

@@ -2,6 +2,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Sockets;
@@ -103,5 +104,19 @@ namespace Netch.Utils
return "";
}
}
public static bool IsZipValid(string path)
{
try
{
using var zipFile = ZipFile.OpenRead(path);
_ = zipFile.Entries;
return true;
}
catch (InvalidDataException)
{
return false;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
@@ -8,7 +9,8 @@ namespace Netch.Utils
{
public class WebUtil
{
private const string DefaultUserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
public const string DefaultUserAgent =
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
private static int DefaultGetTimeout => Global.Settings.RequestTimeout;
@@ -24,53 +26,51 @@ namespace Netch.Utils
return req;
}
/// <summary>
/// 异步下载
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
/// <exception cref="WebException"></exception>
public static async Task<string> DownloadStringAsync(HttpWebRequest req)
public static async Task<byte[]> DownloadBytesAsync(HttpWebRequest req)
{
string content;
var response = (HttpWebResponse) await req.GetResponseAsync();
using (var responseStream = response.GetResponseStream())
{
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
{
content = await sr.ReadToEndAsync();
}
}
using var webResponse = (HttpWebResponse) await req.GetResponseAsync();
using var memoryStream = new MemoryStream();
using var input = webResponse.GetResponseStream();
response.Close();
return content;
await input.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
/// <summary>
/// 异步下载并编码为字符串
/// </summary>
/// <param name="req"></param>
/// <param name="encoding">编码默认UTF-8</param>
/// <returns></returns>
/// <exception cref="WebException"></exception>
public static string DownloadString(HttpWebRequest req)
public static async Task<string> DownloadStringAsync(HttpWebRequest req, string encoding = "UTF-8")
{
string content;
var response = (HttpWebResponse) req.GetResponse();
using (var responseStream = response.GetResponseStream())
{
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
{
content = sr.ReadToEnd();
}
}
using var webResponse = await req.GetResponseAsync();
using var responseStream = webResponse.GetResponseStream();
using var streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encoding));
response.Close();
return content;
return await streamReader.ReadToEndAsync();
}
/// <summary>
/// 异步下载到文件
/// </summary>
/// <param name="req"></param>
/// <param name="fileFullPath"></param>
/// <exception cref="WebException"></exception>
/// <returns></returns>
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath)
{
var dir = Path.GetDirectoryName(fileFullPath) ?? throw new ArgumentException();
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
File.WriteAllText(fileFullPath, await DownloadStringAsync(req));
using var webResponse = (HttpWebResponse) await req.GetResponseAsync();
using var input = webResponse.GetResponseStream();
using var fileStream = File.OpenWrite(fileFullPath);
await input.CopyToAsync(fileStream);
fileStream.Flush();
}
}
}

View File

@@ -11,7 +11,6 @@ namespace Netch.Utils
{
public static class i18N
{
/// <summary>
/// 数据
/// </summary>
@@ -50,6 +49,12 @@ namespace Netch.Utils
// 从外置文件中加载语言
text = File.ReadAllText($"i18n\\{langCode}");
}
else
{
Logging.Error($"无法找到语言 {langCode}, 使用系统语言");
// 加载系统语言
LangCode = CultureInfo.CurrentCulture.Name;
}
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(text);

2
NetchUpdater/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/bin
/obj

View File

@@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<StartupObject>NetchUpdater.Program</StartupObject>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<LangVersion>latest</LangVersion>
<PackageProjectUrl>https://github.com/NetchX/Netch</PackageProjectUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors />
<NoWarn />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn />
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors />
<OutputPath>bin\x64\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>

289
NetchUpdater/Program.cs Normal file
View File

@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NetchUpdater.Properties;
namespace NetchUpdater
{
internal class Program
{
static string UpdaterFullName;
static string UpdaterDirectory;
static string UpdaterFriendlyName;
static Process CurrentProcess;
static Program()
{
CurrentProcess = Process.GetCurrentProcess();
UpdaterFullName = CurrentProcess.MainModule.FileName;
UpdaterDirectory = Path.GetDirectoryName(UpdaterFullName);
UpdaterFriendlyName = Path.GetFileName(UpdaterFullName);
}
public static void Main(string[] args)
{
var result = false;
try
{
#region Check Arguments
if (CurrentProcess.MainModule == null)
{
Console.WriteLine("Current Process MainModule is null");
return;
}
if (args.Length != 3)
{
Console.WriteLine("The program is not user-oriented\n此程序不是面向用户的");
return;
}
// arg0 port
if (!int.TryParse(args[0], out var port))
{
Console.WriteLine("arg0 Port Parse failed");
return;
}
var updateExtracted = true;
// arg1 update File/Directory
var updatePath = Path.GetFullPath(args[1]);
if (File.Exists(updatePath))
{
updateExtracted = false;
}
else if (!Directory.Exists(updatePath))
{
Console.WriteLine("arg1 update file/directory Not found");
return;
}
// arg2 target Directory
string targetPath;
if (!File.Exists(Path.Combine(targetPath = Path.GetFullPath(args[2]), "Netch.exe")))
{
Console.Write("arg2 Netch Directory doesn't seems right");
return;
}
#region if under target Directory,then rerun in temp directory
if (UpdaterDirectory.StartsWith(targetPath))
{
// Updater 在目标目录下
// 将程序复制到临时目录,传递参数
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var newUpdaterPath = Path.Combine(tempPath, UpdaterFriendlyName);
Directory.CreateDirectory(tempPath);
File.Copy(UpdaterFullName, newUpdaterPath);
Process.Start(new ProcessStartInfo
{
FileName = newUpdaterPath,
Arguments = $"{port} {updatePath} {targetPath}",
WorkingDirectory = tempPath,
UseShellExecute = false
});
result = true;
return;
}
#endregion
#endregion
/*while (!Debugger.IsAttached)
{
Console.WriteLine("Waiting Attach");
Thread.Sleep(1000);
}*/
#region Send Netch Exit command
Process[] _;
if ((_ = Process.GetProcessesByName("Netch")).Any())
{
Console.WriteLine("Found Netch process, Send exit command");
try
{
var udpClient = new UdpClient("127.0.0.1", port);
var sendBytes = Encoding.ASCII.GetBytes("Exit");
udpClient.Send(sendBytes, sendBytes.Length);
}
catch
{
Console.WriteLine("Send command failed");
return;
}
foreach (var proc in _)
{
try
{
proc.WaitForExit();
}
catch (Exception)
{
// ignored
}
}
}
#endregion
var counter = 0;
while (!TestFileFree(Path.Combine(targetPath, "Netch.exe")))
{
// wait 5 sec
if (counter > 25)
{
Console.WriteLine("Waiting Netch exit timeout");
return;
}
Thread.Sleep(200);
counter++;
}
#region Update
if (!updateExtracted)
Extract(updatePath, targetPath, true);
else
MoveDirectory(updatePath, targetPath, true);
#endregion
#region Finished Update,Start Netch
Console.WriteLine("Start Netch");
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(targetPath, "Netch.exe"),
UseShellExecute = true,
});
#endregion
result = true;
}
catch (Exception e)
{
if (e is InvalidDataException)
Console.WriteLine("Archive file Broken");
Console.WriteLine(e.ToString());
}
finally
{
if (!result)
{
Console.WriteLine("Press any key to exit...");
Console.Read();
}
}
}
private static void Extract(string archiveFileName, string destDirName, bool overwrite)
{
archiveFileName = Path.GetFullPath(archiveFileName);
destDirName = Path.GetFullPath(destDirName);
if (!File.Exists("7za.exe"))
File.WriteAllBytes("7za.exe", Resources._7za);
var argument = new StringBuilder();
argument.Append($" x {archiveFileName} -o{destDirName} ");
if (overwrite)
argument.Append(" -y ");
Process.Start(new ProcessStartInfo
{
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = Path.GetFullPath("7za.exe"),
Arguments = argument.ToString()
})?.WaitForExit();
}
private static void MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
{
sourceDirName = Path.GetFullPath(sourceDirName);
destDirName = Path.GetFullPath(destDirName);
if (!overwrite)
{
Directory.Move(sourceDirName, destDirName);
}
else
{
var DirStack = new Stack<string>();
DirStack.Push(sourceDirName);
while (DirStack.Count > 0)
{
var DirInfo = new DirectoryInfo(DirStack.Pop());
try
{
foreach (var DirChildInfo in DirInfo.GetDirectories())
{
try
{
var destPath = DirChildInfo.ToString().Replace(sourceDirName, destDirName);
if (!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
}
catch (Exception e)
{
Console.WriteLine($"Create {DirChildInfo} Folder Error: {e.Message}");
}
DirStack.Push(DirChildInfo.FullName);
}
foreach (var FileChildInfo in DirInfo.GetFiles())
{
try
{
var destPath = FileChildInfo.ToString().Replace(sourceDirName, destDirName);
if (File.Exists(destPath))
{
File.Delete(destPath);
}
FileChildInfo.MoveTo(destDirName);
}
catch (Exception e)
{
Console.WriteLine($"Move {FileChildInfo} Error: {e.Message}");
}
}
}
catch (Exception e)
{
Console.WriteLine($"ERROR:{e.Message}");
}
}
}
}
private static bool TestFileFree(string FileName)
{
try
{
File.Move(FileName, FileName);
return true;
}
catch
{
return false;
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NetchUpdater")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NetchUpdater")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("828318A8-9B90-4A5F-BD6B-E632CC9D8933")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace NetchUpdater.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NetchUpdater.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性
/// 重写当前线程的 CurrentUICulture 属性。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] _7za {
get {
object obj = ResourceManager.GetObject("7za", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

Binary file not shown.

View File

@@ -17,6 +17,7 @@ Game accelerator
- [Netch](#netch)
- [TOC](#toc)
- [Description](#description)
- [Sponsor](#sponsor)
- [Donate](#donate)
- [Screenshots](#screenshots)
- [Requirements](#requirements)
@@ -27,6 +28,13 @@ Netch is an open source game accelerator. Unlike SSTap, which needs to add rules
As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an NATTypeTester to test out what your NAT type is. When using SSTap to speed up some P2P gaming connections or the game is required for that kind of open NAT type, you may experience some bad situations such as unable to join the game.
## Sponsor
- [RabbitHosts](https://rabbithosts.com/cart.php)
- [ManSora](https://www.mansora.co/cart.php)
- [ExCloud](https://excloud.net/cart.php)
- [NyanCat](https://nyancat.info/register)
- [YoYu](https://home.yoyu.ltd/cart.php)
## Donate
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*

View File

@@ -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
当前版本已添加配置编辑功能,根据自己的情况,使用订阅或者别的方法添加代理配置,我这里使用的是剪贴板导入
![剪贴板导入](https://raw.githubusercontent.com/BingLingGroup/BingLingGroup.github.io/img/Netch_guide/2019-06-24_210438.png)
![剪贴板导入](screenshots\advanced\importServer.png)
如果你发现你的程序没我截图的看起来清晰,可以右键 `Netch.exe - 属性 - 兼容性 - 更改高 DPI 设置 - 替代高 DPI 缩放执行 - 系统(增强)`
@@ -173,24 +167,24 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
接着点击菜单栏上的`模式 - 创建进程模式`
![模式 - 创建进程模式](https://raw.githubusercontent.com/BingLingGroup/BingLingGroup.github.io/img/Netch_guide/2019-06-24%20211537.png)
![模式 - 创建进程模式](screenshots\advanced\createMode.png)
### 扫描
在弹出的窗口中点击`扫描`
![扫描](https://raw.githubusercontent.com/BingLingGroup/BingLingGroup.github.io/img/Netch_guide/2019-06-24%20211842.png)
![扫描](screenshots\advanced\modeForm.png)
选择你要加速的游戏的安装路径,根据游戏不同,可能需要选择多个不同的目录进行扫描,参见[萌鹰的 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游戏根目录即可
![选择路径](https://raw.githubusercontent.com/NetchX/Netch/master/docs/screenshots/Browse_For_Folder.png)
![选择路径](screenshots\advanced\scan.png)
扫描时可能需要稍等片刻,扫描后记得填写备注
@@ -198,13 +192,13 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
之后点保存进行`保存`
![保存](https://raw.githubusercontent.com/BingLingGroup/BingLingGroup.github.io/img/Netch_guide/2019-06-24%20212837.png)
![保存](screenshots\advanced\saveMode.png)
### 启动
最后确认服务器一栏和模式一栏均为之前自己添加并需要使用的,没问题后点击`启动`即可
![启动](https://raw.githubusercontent.com/BingLingGroup/BingLingGroup.github.io/img/Netch_guide/2019-06-24%20213121.png)
![启动](screenshots\advanced\started.png)
启动后,你再去游戏根目录或者别的启动器如 SteamUplay 启动游戏即可。此时游戏就已经被代理了

View File

@@ -1,5 +1,5 @@
# 新手入门
**Version : 1.4.10**
**Version : 1.5.1**
[下载地址](https://github.com/NetchX/Netch/releases)
@@ -7,36 +7,42 @@
![主界面](screenshots/main.zh-CN.png)
![主界面部分功能介绍](screenshots/main.zh-CN2.png)
## 设置界面
![设置](screenshots/set.png)
## 添加服务器
> Netch 目前仅支持以下代理协议ShadowsocksVMessSocks5ShadowsockRTrojan。
首先,点击`服务器`增加所需服务器
<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 (逃~~

View File

@@ -9,6 +9,7 @@
- [Netch](#Netch)
- [TOC](#TOC)
- [简介](#简介)
- [赞助商](#赞助商)
- [捐赠](#捐赠)
- [新手入门](Quickstart.zh-CN.md)
- [进阶用法](Advanced_Usage.zh-CN.md)
@@ -23,6 +24,13 @@ Netch 是一款 Windows 平台的开源游戏加速工具Netch 可以实现
需要更多特性请移步魔改仓库 [Netch-ForOwnUse](https://github.com/AmazingDM/Netch-ForOwnUse)
## 赞助商
- [RabbitHosts](https://rabbithosts.com/cart.php)
- [ManSora](https://www.mansora.co/cart.php)
- [ExCloud](https://excloud.net/cart.php)
- [NyanCat](https://nyancat.info/register)
- [YoYu](https://home.yoyu.ltd/cart.php)
## 捐赠
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/screenshots/set.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB