mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
60 Commits
1.8.2
...
1.8.3-Beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2bcdc8840 | ||
|
|
4202c8ac5a | ||
|
|
db765d60f0 | ||
|
|
28c248ea70 | ||
|
|
f818c444a4 | ||
|
|
d260afd49b | ||
|
|
66bfe39674 | ||
|
|
95d1b039cd | ||
|
|
b2a7d4fd59 | ||
|
|
ec6b9a2c18 | ||
|
|
dcb90ccdcd | ||
|
|
5da5daa112 | ||
|
|
5d5ee40cd6 | ||
|
|
32d3e97288 | ||
|
|
452c5ec67c | ||
|
|
9d2fd2cce3 | ||
|
|
5a3295d10c | ||
|
|
8269948288 | ||
|
|
8e545fc05f | ||
|
|
9a3a1e3664 | ||
|
|
3f4a31dac8 | ||
|
|
7c0088cc7f | ||
|
|
1a28d791b7 | ||
|
|
05df786ab6 | ||
|
|
1ffbae6135 | ||
|
|
8ca9d6d9be | ||
|
|
4f1ae20b9b | ||
|
|
15a1db3b21 | ||
|
|
54243a80e7 | ||
|
|
947bf2b3ca | ||
|
|
2a165c79df | ||
|
|
55280df299 | ||
|
|
af48e7119e | ||
|
|
a1b978a22c | ||
|
|
d08a9d5bfd | ||
|
|
c69c40750a | ||
|
|
77376502b7 | ||
|
|
fdfc3f11eb | ||
|
|
0d956efac6 | ||
|
|
3f9709167d | ||
|
|
425e468f78 | ||
|
|
a485a4647c | ||
|
|
0b484face4 | ||
|
|
e0b5b0e49c | ||
|
|
f519850ffc | ||
|
|
4513a68e73 | ||
|
|
95de42e778 | ||
|
|
18168c3a4e | ||
|
|
77f2b761fc | ||
|
|
9bd02ec122 | ||
|
|
cfb4a5b3f6 | ||
|
|
6178045f15 | ||
|
|
4773de99e5 | ||
|
|
eb713db867 | ||
|
|
15f4895c0f | ||
|
|
afbda60dfb | ||
|
|
f51229f2c8 | ||
|
|
26f9ae3958 | ||
|
|
dfc680f0b7 | ||
|
|
5e56556534 |
30
.github/workflows/ci.yml
vendored
Normal file
30
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Netch CI
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
dependabot/**
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build Solution
|
||||
shell: pwsh
|
||||
run: .\BUILD.ps1
|
||||
|
||||
- name: Upload Artifact
|
||||
continue-on-error: true
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Netch
|
||||
path: Netch\bin\x64\Release
|
||||
@@ -1,5 +1,8 @@
|
||||
name: Netch CI
|
||||
on: [push, pull_request]
|
||||
name: Netch Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*.*'
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
@@ -17,14 +20,6 @@ jobs:
|
||||
shell: pwsh
|
||||
run: .\BUILD.ps1
|
||||
|
||||
- name: Upload Artifact
|
||||
continue-on-error: true
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Netch
|
||||
path: Netch\bin\x64\Release
|
||||
|
||||
- name: Package
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
@@ -39,10 +34,8 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
name: ${{ env.GITHUB_TAG_NAME }}
|
||||
prerelease: true
|
||||
prerelease: ${{ contains(github.ref, '-') }}
|
||||
draft: false
|
||||
files: |
|
||||
C:\builtfiles\Netch.7z
|
||||
@@ -55,4 +48,4 @@ jobs:
|
||||
## 校验和
|
||||
| 文件名 | SHA256 |
|
||||
| :- | :- |
|
||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/.vs
|
||||
/packages
|
||||
.vs/
|
||||
.idea/
|
||||
/*.user
|
||||
*/bin/
|
||||
*/obj/
|
||||
*.csproj.user
|
||||
9
Interop.nfapinet/Interop.nfapinet.csproj
Normal file
9
Interop.nfapinet/Interop.nfapinet.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,6 +2,8 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable All
|
||||
|
||||
namespace nfapinet
|
||||
{
|
||||
public enum NF_STATUS
|
||||
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SearchComboBox", "SearchCom
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest\UnitTest.csproj", "{53397641-35CA-4336-8E22-2CE12EF476AC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interop.nfapinet", "Interop.nfapinet\Interop.nfapinet.csproj", "{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -26,6 +28,10 @@ Global
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Debug|x64.Build.0 = Debug|x64
|
||||
{53397641-35CA-4336-8E22-2CE12EF476AC}.Release|x64.ActiveCfg = Release|x64
|
||||
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2C968ADF-4822-46A9-A7D9-D05A61CB14DE}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
3
Netch/.gitignore
vendored
3
Netch/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
/bin
|
||||
/obj
|
||||
/Netch.csproj.user
|
||||
16
Netch/Constants.cs
Normal file
16
Netch/Constants.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Netch
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string EOF = "\r\n";
|
||||
public const string UserACL = "data\\user.acl";
|
||||
public const string BuiltinACL = "bin\\default.acl";
|
||||
|
||||
public static class Parameter
|
||||
{
|
||||
public const string Show = "-show";
|
||||
public const string ForceUpdate = "-forceUpdate";
|
||||
public const string Console = "-console";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,12 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 成功启动关键词
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<string> StartedKeywords { get; } = new List<string>();
|
||||
protected virtual IEnumerable<string> StartedKeywords { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 启动失败关键词
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<string> StoppedKeywords { get; } = new List<string>();
|
||||
protected virtual IEnumerable<string> StoppedKeywords { get; set; } = new List<string>();
|
||||
|
||||
public abstract string Name { get; }
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Netch.Controllers
|
||||
public void Start(in Mode mode)
|
||||
{
|
||||
PrivoxyController.Start(MainController.Server!);
|
||||
Global.Job.AddProcess(PrivoxyController.Instance!);
|
||||
string? pacUrl = null;
|
||||
|
||||
if (MainController.Server is Socks5 or Trojan && mode.BypassChina || (Global.Settings.AlwaysStartPACServer ?? false))
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using static Netch.Utils.PortHelper;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -105,20 +104,11 @@ namespace Netch.Controllers
|
||||
{
|
||||
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||
|
||||
if (controller is Guard instanceController)
|
||||
Utils.Utils.KillProcessByName(instanceController.MainFile);
|
||||
|
||||
PortCheck(controller.Socks5LocalPort(), "Socks5");
|
||||
TryReleaseTcpPort(controller.Socks5LocalPort(), "Socks5");
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
|
||||
|
||||
controller.Start(in server, mode);
|
||||
if (controller is Guard {Instance: { }} guard)
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
Global.Job.AddProcess(guard.Instance!);
|
||||
});
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
@@ -133,19 +123,17 @@ namespace Netch.Controllers
|
||||
|
||||
private static void StartMode(Mode mode)
|
||||
{
|
||||
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
|
||||
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
|
||||
|
||||
if (ModeController == null)
|
||||
return;
|
||||
|
||||
if (port != null)
|
||||
PortCheck((ushort) port, portName, portType);
|
||||
TryReleaseTcpPort((ushort) port, portName);
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
||||
|
||||
ModeController.Start(mode);
|
||||
if (ModeController is Guard {Instance: { }} guard)
|
||||
Global.Job.AddProcess(guard.Instance!);
|
||||
}
|
||||
|
||||
public static async Task StopAsync()
|
||||
@@ -189,7 +177,7 @@ namespace Netch.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckPort(port, portType);
|
||||
PortHelper.CheckPort(port, portType);
|
||||
}
|
||||
catch (PortInUseException)
|
||||
{
|
||||
@@ -200,6 +188,35 @@ namespace Netch.Controllers
|
||||
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryReleaseTcpPort(ushort port, string portName)
|
||||
{
|
||||
foreach (var p in PortHelper.GetProcessByUsedTcpPort(port))
|
||||
{
|
||||
string fileName;
|
||||
try
|
||||
{
|
||||
fileName = p.MainModule!.FileName;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning(e.ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileName.StartsWith(Global.NetchDir))
|
||||
{
|
||||
p.Kill();
|
||||
p.WaitForExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MessageException(i18N.TranslateFormat("The {0} port is used by {1}.", $"{portName} ({port})", $"({p.Id}){fileName}"));
|
||||
}
|
||||
}
|
||||
|
||||
PortCheck(port, portName, PortType.TCP);
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageException : Exception
|
||||
|
||||
@@ -17,57 +17,30 @@ namespace Netch.Controllers
|
||||
{
|
||||
private static readonly ServiceController NFService = new("netfilter2");
|
||||
|
||||
private static readonly string BinDriver;
|
||||
private const string BinDriver = "bin\\nfdriver.sys";
|
||||
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
|
||||
|
||||
static NFController()
|
||||
{
|
||||
string fileName;
|
||||
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
|
||||
{
|
||||
case "10.0":
|
||||
case "6.3":
|
||||
case "6.2":
|
||||
case "6.1":
|
||||
case "6.0":
|
||||
fileName = "nfdriver.sys";
|
||||
break;
|
||||
default:
|
||||
throw new MessageException($"不支持的系统版本:{Environment.OSVersion.Version}");
|
||||
}
|
||||
|
||||
BinDriver = "bin\\" + fileName;
|
||||
}
|
||||
|
||||
public string Name { get; } = "Redirector";
|
||||
|
||||
public void Start(in Mode mode)
|
||||
{
|
||||
CheckDriver();
|
||||
|
||||
#region aio_dial
|
||||
aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false");
|
||||
aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
|
||||
|
||||
aio_dial((int)NameList.TYPE_FILTERLOOPBACK, "false");
|
||||
aio_dial((int)NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
|
||||
// Server
|
||||
aio_dial((int) NameList.TYPE_FILTERUDP, (Global.Settings.ProcessProxyProtocol != PortType.TCP).ToString().ToLower());
|
||||
aio_dial((int) NameList.TYPE_FILTERTCP, (Global.Settings.ProcessProxyProtocol != PortType.UDP).ToString().ToLower());
|
||||
dial_Server(Global.Settings.ProcessProxyProtocol);
|
||||
|
||||
aio_dial((int)NameList.TYPE_FILTERUDP, (Global.Settings.ProcessProxyProtocol != PortType.TCP).ToString().ToLower());
|
||||
aio_dial((int)NameList.TYPE_FILTERTCP, (Global.Settings.ProcessProxyProtocol != PortType.UDP).ToString().ToLower());
|
||||
SetServer(Global.Settings.ProcessProxyProtocol);
|
||||
// Mode Rule
|
||||
dial_Name(mode);
|
||||
|
||||
if (!CheckRule(mode.FullRule, out var list))
|
||||
throw new MessageException($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
|
||||
|
||||
SetName(mode);
|
||||
|
||||
#endregion
|
||||
|
||||
if (Global.Settings.RedirectDNS)
|
||||
aio_dial((int)NameList.TYPE_REDIRCTOR_DNS, Global.Settings.RedirectDNSAddr.ToString());
|
||||
|
||||
if (Global.Settings.RedirectICMP)
|
||||
aio_dial((int)NameList.TYPE_REDIRCTOR_ICMP, Global.Settings.RedirectICMPAddr.ToString());
|
||||
|
||||
aio_dial((int)NameList.TYPE_FILTERCHILDPROC, Global.Settings.ChildProcessHandle.ToString());
|
||||
// Features
|
||||
aio_dial((int) NameList.TYPE_REDIRCTOR_DNS, Global.Settings.RedirectDNS ? Global.Settings.RedirectDNSAddr : "");
|
||||
aio_dial((int) NameList.TYPE_REDIRCTOR_ICMP, Global.Settings.RedirectICMP ? Global.Settings.RedirectICMPAddr : "");
|
||||
aio_dial((int) NameList.TYPE_FILTERCHILDPROC, Global.Settings.ChildProcessHandle.ToString().ToLower());
|
||||
|
||||
if (!aio_init())
|
||||
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
|
||||
@@ -78,83 +51,54 @@ namespace Netch.Controllers
|
||||
aio_free();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="rules"></param>
|
||||
/// <param name="incompatibleRule"></param>
|
||||
/// <returns>No Problem true</returns>
|
||||
public static bool CheckRule(IEnumerable<string> rules, out IEnumerable<string> incompatibleRule)
|
||||
{
|
||||
incompatibleRule = rules.Where(r => !CheckCppRegex(r, false));
|
||||
aio_dial((int)NameList.TYPE_CLRNAME, "");
|
||||
return !incompatibleRule.Any();
|
||||
}
|
||||
#region CheckRule
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="clear"></param>
|
||||
/// <returns>No Problem true</returns>
|
||||
public static bool CheckCppRegex(string r, bool clear = true)
|
||||
private static bool CheckCppRegex(string r, bool clear = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (r.StartsWith("!"))
|
||||
return aio_dial((int)NameList.TYPE_ADDNAME, r.Substring(1));
|
||||
return aio_dial((int) NameList.TYPE_ADDNAME, r.Substring(1));
|
||||
|
||||
return aio_dial((int)NameList.TYPE_ADDNAME, r);
|
||||
return aio_dial((int) NameList.TYPE_ADDNAME, r);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (clear)
|
||||
aio_dial((int)NameList.TYPE_CLRNAME, "");
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckDriver()
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="rules"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns>No Problem true</returns>
|
||||
public static bool CheckRules(IEnumerable<string> rules, out IEnumerable<string> results)
|
||||
{
|
||||
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
|
||||
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
||||
|
||||
Logging.Info("内置驱动版本: " + binFileVersion);
|
||||
Logging.Info("系统驱动版本: " + systemFileVersion);
|
||||
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
InstallDriver();
|
||||
return;
|
||||
}
|
||||
|
||||
var reinstallFlag = false;
|
||||
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
|
||||
{
|
||||
if (binResult.CompareTo(systemResult) > 0)
|
||||
// Bin greater than Installed
|
||||
reinstallFlag = true;
|
||||
else if (systemResult.Major != binResult.Major)
|
||||
// Installed greater than Bin but Major Version Difference (has breaking changes), do downgrade
|
||||
reinstallFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!systemFileVersion.Equals(binFileVersion))
|
||||
reinstallFlag = true;
|
||||
}
|
||||
|
||||
if (!reinstallFlag)
|
||||
return;
|
||||
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
InstallDriver();
|
||||
results = rules.Where(r => !CheckCppRegex(r, false));
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
return !results.Any();
|
||||
}
|
||||
|
||||
private void SetServer(in PortType portType)
|
||||
public static string GenerateInvalidRulesMessage(IEnumerable<string> rules)
|
||||
{
|
||||
return $"{string.Join("\n", rules)}\nAbove rules does not conform to C++ regular expression syntax";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void dial_Server(in PortType portType)
|
||||
{
|
||||
if (portType == PortType.Both)
|
||||
{
|
||||
SetServer(PortType.TCP);
|
||||
SetServer(PortType.UDP);
|
||||
dial_Server(PortType.TCP);
|
||||
dial_Server(PortType.UDP);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,105 +121,97 @@ namespace Netch.Controllers
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
aio_dial((int)NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
|
||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
||||
aio_dial((int)NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
aio_dial((int) NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
}
|
||||
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS)
|
||||
{
|
||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
|
||||
aio_dial((int)NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod ?? string.Empty);
|
||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, shadowsocks.Password ?? string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
|
||||
}
|
||||
else
|
||||
{
|
||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
||||
aio_dial((int)NameList.TYPE_TCPUSER + offset, string.Empty);
|
||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, string.Empty);
|
||||
aio_dial((int)NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
||||
aio_dial((int) NameList.TYPE_TCPUSER + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPPASS + offset, string.Empty);
|
||||
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetName(Mode mode)
|
||||
private void dial_Name(Mode mode)
|
||||
{
|
||||
aio_dial((int)NameList.TYPE_CLRNAME, "");
|
||||
foreach (var rule in mode.FullRule)
|
||||
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||
var list = new List<string>();
|
||||
foreach (var s in mode.FullRule)
|
||||
{
|
||||
if (rule.StartsWith("!"))
|
||||
if (s.StartsWith("!"))
|
||||
{
|
||||
aio_dial((int)NameList.TYPE_BYPNAME, rule.Substring(1));
|
||||
if (!aio_dial((int) NameList.TYPE_BYPNAME, s.Substring(1)))
|
||||
list.Add(s);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
aio_dial((int)NameList.TYPE_ADDNAME, rule);
|
||||
if (!aio_dial((int) NameList.TYPE_ADDNAME, s))
|
||||
list.Add(s);
|
||||
}
|
||||
|
||||
aio_dial((int)NameList.TYPE_ADDNAME, @"NTT\.exe");
|
||||
aio_dial((int)NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
|
||||
if (list.Any())
|
||||
throw new MessageException(GenerateInvalidRulesMessage(list));
|
||||
|
||||
aio_dial((int) NameList.TYPE_ADDNAME, @"NTT\.exe");
|
||||
aio_dial((int) NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
|
||||
}
|
||||
|
||||
#region NativeMethods
|
||||
#region DriverUtil
|
||||
|
||||
private const int UdpNameListOffset = (int)NameList.TYPE_UDPTYPE - (int)NameList.TYPE_TCPTYPE;
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_init();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_free();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern ulong aio_getUP();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern ulong aio_getDL();
|
||||
|
||||
public enum NameList
|
||||
private static void CheckDriver()
|
||||
{
|
||||
//bool
|
||||
TYPE_FILTERLOOPBACK,
|
||||
TYPE_FILTERTCP,
|
||||
TYPE_FILTERUDP,
|
||||
TYPE_FILTERIP,
|
||||
TYPE_FILTERCHILDPROC,//子进程捕获
|
||||
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
|
||||
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
||||
|
||||
TYPE_TCPLISN,
|
||||
TYPE_TCPTYPE,
|
||||
TYPE_TCPHOST,
|
||||
TYPE_TCPUSER,
|
||||
TYPE_TCPPASS,
|
||||
TYPE_TCPMETH,
|
||||
Logging.Info("内置驱动版本: " + binFileVersion);
|
||||
Logging.Info("系统驱动版本: " + systemFileVersion);
|
||||
|
||||
TYPE_UDPTYPE,
|
||||
TYPE_UDPHOST,
|
||||
TYPE_UDPUSER,
|
||||
TYPE_UDPPASS,
|
||||
TYPE_UDPMETH,
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
// Install
|
||||
InstallDriver();
|
||||
return;
|
||||
}
|
||||
|
||||
TYPE_ADDNAME,
|
||||
TYPE_ADDFIP,
|
||||
var reinstall = false;
|
||||
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
|
||||
{
|
||||
if (binResult.CompareTo(systemResult) > 0)
|
||||
// Update
|
||||
reinstall = true;
|
||||
else if (systemResult.Major != binResult.Major)
|
||||
// Downgrade when Major version different (may have breaking changes)
|
||||
reinstall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse File versionName to Version failed
|
||||
if (!systemFileVersion.Equals(binFileVersion))
|
||||
// versionNames are different, Reinstall
|
||||
reinstall = true;
|
||||
}
|
||||
|
||||
TYPE_BYPNAME,
|
||||
if (!reinstall)
|
||||
return;
|
||||
|
||||
TYPE_CLRNAME,
|
||||
TYPE_CLRFIP,
|
||||
|
||||
//str addr x.x.x.x only ipv4
|
||||
TYPE_REDIRCTOR_DNS,
|
||||
TYPE_REDIRCTOR_ICMP
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
InstallDriver();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
|
||||
/// <summary>
|
||||
/// 安装 NF 驱动
|
||||
/// </summary>
|
||||
@@ -341,5 +277,61 @@ namespace Netch.Controllers
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NativeMethods
|
||||
|
||||
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_init();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool aio_free();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern ulong aio_getUP();
|
||||
|
||||
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern ulong aio_getDL();
|
||||
|
||||
public enum NameList
|
||||
{
|
||||
//bool
|
||||
TYPE_FILTERLOOPBACK,
|
||||
TYPE_FILTERTCP,
|
||||
TYPE_FILTERUDP,
|
||||
TYPE_FILTERIP,
|
||||
TYPE_FILTERCHILDPROC, //子进程捕获
|
||||
|
||||
TYPE_TCPLISN,
|
||||
TYPE_TCPTYPE,
|
||||
TYPE_TCPHOST,
|
||||
TYPE_TCPUSER,
|
||||
TYPE_TCPPASS,
|
||||
TYPE_TCPMETH,
|
||||
|
||||
TYPE_UDPTYPE,
|
||||
TYPE_UDPHOST,
|
||||
TYPE_UDPUSER,
|
||||
TYPE_UDPPASS,
|
||||
TYPE_UDPMETH,
|
||||
|
||||
TYPE_ADDNAME,
|
||||
TYPE_ADDFIP,
|
||||
|
||||
TYPE_BYPNAME,
|
||||
|
||||
TYPE_CLRNAME,
|
||||
TYPE_CLRFIP,
|
||||
|
||||
//str addr x.x.x.x only ipv4
|
||||
TYPE_REDIRCTOR_DNS,
|
||||
TYPE_REDIRCTOR_ICMP
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Netch.Controllers
|
||||
|
||||
public override string MainFile { get; protected set; } = "pcap2socks.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"└"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"└"};
|
||||
|
||||
private readonly OutboundAdapter _outbound = new();
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Netch.Controllers
|
||||
Global.MainForm.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (!_form!.IsDisposed)
|
||||
_form!.richTextBox1.AppendText(line + "\n");
|
||||
_form.richTextBox1.AppendText(line + "\n");
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public DNSController DNSController = new();
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"Running"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"Running"};
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"failed", "invalid vconfig file"};
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"failed", "invalid vconfig file"};
|
||||
|
||||
public override string MainFile { get; protected set; } = "tun2socks.exe";
|
||||
|
||||
@@ -61,23 +61,42 @@ namespace Netch.Controllers
|
||||
dns = new List<string> {"127.0.0.1"};
|
||||
}
|
||||
|
||||
SetupRouteTable(mode);
|
||||
var parameter = new Tun2SocksParameter
|
||||
{
|
||||
tunAddr = Global.Settings.TUNTAP.Address,
|
||||
tunMask = Global.Settings.TUNTAP.Netmask,
|
||||
tunGw = Global.Settings.TUNTAP.Gateway,
|
||||
tunDns = DnsUtils.Join(dns),
|
||||
tunName = TUNTAP.GetName(_tap.ComponentID),
|
||||
fakeDns = Global.Settings.TUNTAP.UseFakeDNS && Flags.SupportFakeDns
|
||||
};
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", Name));
|
||||
|
||||
var argument = new StringBuilder();
|
||||
if (server is Socks5 socks5 && !socks5.Auth())
|
||||
argument.Append($"-proxyServer {server.AutoResolveHostname()}:{server.Port} ");
|
||||
parameter.proxyServer = $"{server.AutoResolveHostname()}:{server.Port}";
|
||||
else
|
||||
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
|
||||
parameter.proxyServer = $"127.0.0.1:{Global.Settings.Socks5LocalPort}";
|
||||
|
||||
argument.Append(
|
||||
$"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {DnsUtils.Join(dns)} -tunName \"{TUNTAP.GetName(_tap.ComponentID)}\" ");
|
||||
StartInstanceAuto(parameter.ToString(), ProcessPriorityClass.RealTime);
|
||||
|
||||
if (Global.Settings.TUNTAP.UseFakeDNS && Global.Flags.SupportFakeDns)
|
||||
argument.Append("-fakeDns ");
|
||||
SetupRouteTable(mode);
|
||||
}
|
||||
|
||||
StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime);
|
||||
[Verb]
|
||||
public class Tun2SocksParameter : ParameterBase
|
||||
{
|
||||
public string proxyServer { get; set; }
|
||||
|
||||
public string tunAddr { get; set; }
|
||||
|
||||
public string tunMask { get; set; }
|
||||
|
||||
public string tunGw { get; set; }
|
||||
|
||||
public string tunDns { get; set; }
|
||||
|
||||
public string tunName { get; set; }
|
||||
|
||||
public bool fakeDns { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,14 +146,7 @@ namespace Netch.Controllers
|
||||
// 绕过规则 IP
|
||||
|
||||
// 将 TUN/TAP 网卡权重放到最高
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = $"interface ip set interface {_tap.Index} metric=0",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
SetInterface(RouteType.TUNTAP, 0);
|
||||
|
||||
Logging.Info("绕行 → 规则 IP");
|
||||
RouteAction(Action.Create, mode.FullRule, RouteType.Outbound);
|
||||
@@ -157,6 +169,29 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private void SetInterface(RouteType routeType, int? metric = null)
|
||||
{
|
||||
IAdapter adapter = routeType switch
|
||||
{
|
||||
RouteType.Outbound => _outbound,
|
||||
RouteType.TUNTAP => _tap,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null)
|
||||
};
|
||||
|
||||
var arguments = $"interface ip set interface {adapter.Index} ";
|
||||
if (metric != null)
|
||||
arguments += $"metric={metric} ";
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = arguments,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除绕行规则
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -18,8 +19,8 @@ namespace Netch.Controllers
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019 - 2021";
|
||||
|
||||
public const string AssemblyVersion = @"1.8.2";
|
||||
private const string Suffix = @"";
|
||||
public const string AssemblyVersion = @"1.8.3";
|
||||
private const string Suffix = @"Beta4";
|
||||
|
||||
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
||||
|
||||
@@ -89,5 +90,19 @@ namespace Netch.Controllers
|
||||
fileName = match.Groups["filename"].Value;
|
||||
sha256 = match.Groups["sha256"].Value;
|
||||
}
|
||||
|
||||
public static string GetLatestReleaseContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (string l in LatestRelease.body.GetLines(false).SkipWhile(l => l.FirstOrDefault() != '#'))
|
||||
{
|
||||
if (l.Contains("校验和"))
|
||||
break;
|
||||
|
||||
sb.AppendLine(l);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
Netch/Flags.cs
Normal file
16
Netch/Flags.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using Netch.Controllers;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
public static class Flags
|
||||
{
|
||||
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
|
||||
|
||||
private static readonly Lazy<bool> LazySupportFakeDns = new(() => new TUNTAPController().TestFakeDNS());
|
||||
|
||||
public static bool SupportFakeDns => LazySupportFakeDns.Value;
|
||||
|
||||
public static bool AlwaysShowNewVersionFound { get; set; }
|
||||
}
|
||||
}
|
||||
116
Netch/Forms/MainForm.Designer.cs
generated
116
Netch/Forms/MainForm.Designer.cs
generated
@@ -29,14 +29,12 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.MenuStrip = new System.Windows.Forms.MenuStrip();
|
||||
this.ServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ImportServersFromClipboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CreateRouteTableRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ManageSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UpdateServersFromSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -95,13 +93,13 @@
|
||||
this.ConfigurationGroupBox.SuspendLayout();
|
||||
this.configLayoutPanel.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize) (this.EditServerPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.CopyLinkPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.DeleteServerPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.SpeedPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.EditServerPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.CopyLinkPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DeleteServerPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.SpeedPictureBox)).BeginInit();
|
||||
this.tableLayoutPanel3.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize) (this.EditModePictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.DeleteModePictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.EditModePictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DeleteModePictureBox)).BeginInit();
|
||||
this.StatusStrip.SuspendLayout();
|
||||
this.NotifyMenu.SuspendLayout();
|
||||
this.ProfileGroupBox.SuspendLayout();
|
||||
@@ -113,10 +111,16 @@
|
||||
//
|
||||
this.MenuStrip.BackColor = System.Drawing.SystemColors.Control;
|
||||
this.MenuStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.ServerToolStripMenuItem, this.ModeToolStripMenuItem, this.SubscribeToolStripMenuItem, this.OptionsToolStripMenuItem, this.HelpToolStripMenuItem, this.exitToolStripMenuItem, this.AboutToolStripButton, this.NewVersionLabel, this.VersionLabel
|
||||
});
|
||||
this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ServerToolStripMenuItem,
|
||||
this.ModeToolStripMenuItem,
|
||||
this.SubscribeToolStripMenuItem,
|
||||
this.OptionsToolStripMenuItem,
|
||||
this.HelpToolStripMenuItem,
|
||||
this.exitToolStripMenuItem,
|
||||
this.AboutToolStripButton,
|
||||
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;
|
||||
@@ -125,10 +129,8 @@
|
||||
//
|
||||
// ServerToolStripMenuItem
|
||||
//
|
||||
this.ServerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.ImportServersFromClipboardToolStripMenuItem
|
||||
});
|
||||
this.ServerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ImportServersFromClipboardToolStripMenuItem});
|
||||
this.ServerToolStripMenuItem.Margin = new System.Windows.Forms.Padding(3, 0, 0, 1);
|
||||
this.ServerToolStripMenuItem.Name = "ServerToolStripMenuItem";
|
||||
this.ServerToolStripMenuItem.Size = new System.Drawing.Size(57, 21);
|
||||
@@ -143,7 +145,9 @@
|
||||
//
|
||||
// ModeToolStripMenuItem
|
||||
//
|
||||
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {this.CreateProcessModeToolStripMenuItem, this.CreateRouteTableRuleToolStripMenuItem, this.ReloadModesToolStripMenuItem});
|
||||
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.CreateProcessModeToolStripMenuItem,
|
||||
this.CreateRouteTableRuleToolStripMenuItem});
|
||||
this.ModeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
this.ModeToolStripMenuItem.Name = "ModeToolStripMenuItem";
|
||||
this.ModeToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
|
||||
@@ -163,19 +167,12 @@
|
||||
this.CreateRouteTableRuleToolStripMenuItem.Text = "Create Route Table Rule";
|
||||
this.CreateRouteTableRuleToolStripMenuItem.Click += new System.EventHandler(this.createRouteTableModeToolStripMenuItem_Click);
|
||||
//
|
||||
// ReloadModesToolStripMenuItem
|
||||
//
|
||||
this.ReloadModesToolStripMenuItem.Name = "ReloadModesToolStripMenuItem";
|
||||
this.ReloadModesToolStripMenuItem.Size = new System.Drawing.Size(217, 22);
|
||||
this.ReloadModesToolStripMenuItem.Text = "Reload Modes";
|
||||
this.ReloadModesToolStripMenuItem.Click += new System.EventHandler(this.ReloadModesToolStripMenuItem_Click);
|
||||
//
|
||||
// SubscribeToolStripMenuItem
|
||||
//
|
||||
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.ManageSubscribeLinksToolStripMenuItem, this.UpdateServersFromSubscribeLinksToolStripMenuItem, this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem
|
||||
});
|
||||
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ManageSubscribeLinksToolStripMenuItem,
|
||||
this.UpdateServersFromSubscribeLinksToolStripMenuItem,
|
||||
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem});
|
||||
this.SubscribeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
this.SubscribeToolStripMenuItem.Name = "SubscribeToolStripMenuItem";
|
||||
this.SubscribeToolStripMenuItem.Size = new System.Drawing.Size(77, 21);
|
||||
@@ -204,10 +201,15 @@
|
||||
//
|
||||
// OptionsToolStripMenuItem
|
||||
//
|
||||
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.OpenDirectoryToolStripMenuItem, this.CleanDNSCacheToolStripMenuItem, this.UpdateACLToolStripMenuItem, this.updateACLWithProxyToolStripMenuItem, this.updatePACToolStripMenuItem, this.UninstallServiceToolStripMenuItem, this.UninstallTapDriverToolStripMenuItem, this.removeNetchFirewallRulesToolStripMenuItem
|
||||
});
|
||||
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.OpenDirectoryToolStripMenuItem,
|
||||
this.CleanDNSCacheToolStripMenuItem,
|
||||
this.UpdateACLToolStripMenuItem,
|
||||
this.updateACLWithProxyToolStripMenuItem,
|
||||
this.updatePACToolStripMenuItem,
|
||||
this.UninstallServiceToolStripMenuItem,
|
||||
this.UninstallTapDriverToolStripMenuItem,
|
||||
this.removeNetchFirewallRulesToolStripMenuItem});
|
||||
this.OptionsToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
this.OptionsToolStripMenuItem.Name = "OptionsToolStripMenuItem";
|
||||
this.OptionsToolStripMenuItem.Size = new System.Drawing.Size(66, 21);
|
||||
@@ -271,10 +273,9 @@
|
||||
//
|
||||
// HelpToolStripMenuItem
|
||||
//
|
||||
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.CheckForUpdatesToolStripMenuItem, this.fAQToolStripMenuItem
|
||||
});
|
||||
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.CheckForUpdatesToolStripMenuItem,
|
||||
this.fAQToolStripMenuItem});
|
||||
this.HelpToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
this.HelpToolStripMenuItem.Name = "HelpToolStripMenuItem";
|
||||
this.HelpToolStripMenuItem.Size = new System.Drawing.Size(47, 21);
|
||||
@@ -423,7 +424,7 @@
|
||||
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
|
||||
this.ModeComboBox.TabIndex = 2;
|
||||
this.ModeComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
||||
this.ModeComboBox.SelectedIndexChanged += new System.EventHandler(this.ModeComboBox_SelectedIndexChanged);
|
||||
this.ModeComboBox.SelectionChangeCommitted += new System.EventHandler(this.ModeComboBox_SelectionChangeCommitted);
|
||||
//
|
||||
// ServerComboBox
|
||||
//
|
||||
@@ -438,7 +439,7 @@
|
||||
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
|
||||
this.ServerComboBox.TabIndex = 1;
|
||||
this.ServerComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
||||
this.ServerComboBox.SelectedIndexChanged += new System.EventHandler(this.ServerComboBox_SelectedIndexChanged);
|
||||
this.ServerComboBox.SelectionChangeCommitted += new System.EventHandler(this.ServerComboBox_SelectionChangeCommitted);
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
@@ -550,10 +551,14 @@
|
||||
// StatusStrip
|
||||
//
|
||||
this.StatusStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.StatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.StatusLabel, this.UsedBandwidthLabel, this.DownloadSpeedLabel, this.UploadSpeedLabel, this.blankToolStripStatusLabel, this.NatTypeStatusLabel, this.NatTypeStatusLightLabel
|
||||
});
|
||||
this.StatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.StatusLabel,
|
||||
this.UsedBandwidthLabel,
|
||||
this.DownloadSpeedLabel,
|
||||
this.UploadSpeedLabel,
|
||||
this.blankToolStripStatusLabel,
|
||||
this.NatTypeStatusLabel,
|
||||
this.NatTypeStatusLightLabel});
|
||||
this.StatusStrip.Location = new System.Drawing.Point(0, 272);
|
||||
this.StatusStrip.Name = "StatusStrip";
|
||||
this.StatusStrip.Size = new System.Drawing.Size(740, 22);
|
||||
@@ -616,7 +621,7 @@
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.ControlButton.Location = new System.Drawing.Point(631, 3);
|
||||
this.ControlButton.Name = "ControlButton";
|
||||
this.ControlButton.Size = new System.Drawing.Size(75, 27);
|
||||
@@ -635,10 +640,9 @@
|
||||
// NotifyMenu
|
||||
//
|
||||
this.NotifyMenu.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.NotifyMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
|
||||
{
|
||||
this.ShowMainFormToolStripButton, this.ExitToolStripButton
|
||||
});
|
||||
this.NotifyMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ShowMainFormToolStripButton,
|
||||
this.ExitToolStripButton});
|
||||
this.NotifyMenu.Name = "NotifyMenu";
|
||||
this.NotifyMenu.ShowItemToolTips = false;
|
||||
this.NotifyMenu.Size = new System.Drawing.Size(108, 48);
|
||||
@@ -659,7 +663,7 @@
|
||||
//
|
||||
// SettingsButton
|
||||
//
|
||||
this.SettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.SettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.SettingsButton.Location = new System.Drawing.Point(1, 3);
|
||||
this.SettingsButton.Name = "SettingsButton";
|
||||
this.SettingsButton.Size = new System.Drawing.Size(72, 27);
|
||||
@@ -728,7 +732,7 @@
|
||||
this.Controls.Add(this.MenuStrip);
|
||||
this.Controls.Add(this.StatusStrip);
|
||||
this.Controls.Add(this.flowLayoutPanel1);
|
||||
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.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
@@ -744,13 +748,13 @@
|
||||
this.configLayoutPanel.ResumeLayout(false);
|
||||
this.configLayoutPanel.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize) (this.EditServerPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.CopyLinkPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.DeleteServerPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.SpeedPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.EditServerPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.CopyLinkPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DeleteServerPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.SpeedPictureBox)).EndInit();
|
||||
this.tableLayoutPanel3.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize) (this.EditModePictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize) (this.DeleteModePictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.EditModePictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.DeleteModePictureBox)).EndInit();
|
||||
this.StatusStrip.ResumeLayout(false);
|
||||
this.StatusStrip.PerformLayout();
|
||||
this.NotifyMenu.ResumeLayout(false);
|
||||
@@ -760,6 +764,7 @@
|
||||
this.ButtomControlContainerControl.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
private System.Windows.Forms.ToolStripMenuItem CreateRouteTableRuleToolStripMenuItem;
|
||||
@@ -798,7 +803,6 @@
|
||||
private System.Windows.Forms.TableLayoutPanel ProfileTable;
|
||||
private System.Windows.Forms.ToolStripMenuItem UninstallTapDriverToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem CheckForUpdatesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ReloadModesToolStripMenuItem;
|
||||
private System.Windows.Forms.ComboBox ServerComboBox;
|
||||
private System.Windows.Forms.Label ServerLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem ServerToolStripMenuItem;
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace Netch.Forms
|
||||
|
||||
private readonly Dictionary<string, object> _mainFormText = new();
|
||||
|
||||
private bool _comboBoxInitialized;
|
||||
private bool _textRecorded;
|
||||
|
||||
public MainForm()
|
||||
@@ -93,7 +92,6 @@ namespace Netch.Forms
|
||||
|
||||
ModeHelper.Load();
|
||||
LoadModes();
|
||||
_comboBoxInitialized = true;
|
||||
|
||||
// 加载翻译
|
||||
TranslateControls();
|
||||
@@ -264,25 +262,6 @@ namespace Netch.Forms
|
||||
Show();
|
||||
}
|
||||
|
||||
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
ModeHelper.Load();
|
||||
LoadModes();
|
||||
NotifyTip(i18N.Translate("Modes have been reload"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subscription
|
||||
@@ -465,7 +444,7 @@ namespace Netch.Forms
|
||||
if (useProxy)
|
||||
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, Global.UserACL));
|
||||
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, Constants.UserACL));
|
||||
NotifyTip(i18N.Translate("ACL updated successfully"));
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -577,7 +556,8 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
|
||||
if (MessageBoxX.Show(i18N.Translate($"Download and install now?\n\n{UpdateChecker.GetLatestReleaseContent()}"), confirm: true) !=
|
||||
DialogResult.OK)
|
||||
return;
|
||||
|
||||
NotifyTip(i18N.Translate("Start downloading new version"));
|
||||
@@ -631,9 +611,7 @@ namespace Netch.Forms
|
||||
if (!IsWaiting())
|
||||
{
|
||||
// 停止
|
||||
State = State.Stopping;
|
||||
await MainController.StopAsync();
|
||||
State = State.Stopped;
|
||||
await StopAsyncCore();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -735,13 +713,9 @@ namespace Netch.Forms
|
||||
|
||||
private void LoadServers()
|
||||
{
|
||||
var comboBoxInitialized = _comboBoxInitialized;
|
||||
_comboBoxInitialized = false;
|
||||
|
||||
ServerComboBox.Items.Clear();
|
||||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||||
SelectLastServer();
|
||||
_comboBoxInitialized = comboBoxInitialized;
|
||||
}
|
||||
|
||||
public void SelectLastServer()
|
||||
@@ -756,11 +730,8 @@ namespace Netch.Forms
|
||||
// 如果当前 ServerComboBox 中没元素,不做处理
|
||||
}
|
||||
|
||||
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
private void ServerComboBox_SelectionChangeCommitted(object sender, EventArgs o)
|
||||
{
|
||||
if (!_comboBoxInitialized)
|
||||
return;
|
||||
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
@@ -859,14 +830,10 @@ namespace Netch.Forms
|
||||
|
||||
public void LoadModes()
|
||||
{
|
||||
var comboBoxInitialized = _comboBoxInitialized;
|
||||
_comboBoxInitialized = false;
|
||||
|
||||
ModeComboBox.Items.Clear();
|
||||
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
|
||||
ModeComboBox.Items.AddRange(Global.Modes.Cast<object>().ToArray());
|
||||
ModeComboBox.Tag = null;
|
||||
SelectLastMode();
|
||||
_comboBoxInitialized = comboBoxInitialized;
|
||||
}
|
||||
|
||||
public void SelectLastMode()
|
||||
@@ -881,11 +848,8 @@ namespace Netch.Forms
|
||||
// 如果当前 ModeComboBox 中没元素,不做处理
|
||||
}
|
||||
|
||||
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
private void ModeComboBox_SelectionChangeCommitted(object sender, EventArgs o)
|
||||
{
|
||||
if (!_comboBoxInitialized)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
|
||||
@@ -1137,8 +1101,7 @@ namespace Netch.Forms
|
||||
// 启动需要禁用的控件
|
||||
UninstallServiceToolStripMenuItem.Enabled = UpdateACLToolStripMenuItem.Enabled = updateACLWithProxyToolStripMenuItem.Enabled =
|
||||
updatePACToolStripMenuItem.Enabled = UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||||
UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled = UninstallTapDriverToolStripMenuItem.Enabled =
|
||||
ReloadModesToolStripMenuItem.Enabled = enabled;
|
||||
UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled = UninstallTapDriverToolStripMenuItem.Enabled = enabled;
|
||||
}
|
||||
|
||||
_state = value;
|
||||
@@ -1190,6 +1153,27 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StopAsyncCore()
|
||||
{
|
||||
State = State.Stopping;
|
||||
await MainController.StopAsync();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (IsWaiting())
|
||||
return;
|
||||
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(Stop));
|
||||
return;
|
||||
}
|
||||
|
||||
StopAsyncCore().Wait();
|
||||
}
|
||||
|
||||
private bool IsWaiting()
|
||||
{
|
||||
return State == State.Waiting || State == State.Stopped;
|
||||
@@ -1269,7 +1253,7 @@ namespace Netch.Forms
|
||||
{
|
||||
if (natType > 0 && natType < 5)
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = Global.Flags.IsWindows10Upper;
|
||||
NatTypeStatusLightLabel.Visible = Flags.IsWindows10Upper;
|
||||
var c = natType switch
|
||||
{
|
||||
1 => Color.LimeGreen,
|
||||
@@ -1399,8 +1383,8 @@ namespace Netch.Forms
|
||||
if (File.Exists(file))
|
||||
File.Delete(file);
|
||||
|
||||
if (!IsWaiting())
|
||||
await MainController.StopAsync();
|
||||
if (IsWaiting())
|
||||
await StopAsyncCore();
|
||||
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
@@ -1436,6 +1420,8 @@ namespace Netch.Forms
|
||||
{
|
||||
UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound;
|
||||
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
|
||||
if (Flags.AlwaysShowNewVersionFound)
|
||||
OnUpdateCheckerOnNewVersionFound(null!, null!);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1493,8 +1479,14 @@ namespace Netch.Forms
|
||||
|
||||
#region NotifyIcon
|
||||
|
||||
private void ShowMainFormToolStripButton_Click(object sender, EventArgs e)
|
||||
public void ShowMainFormToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => ShowMainFormToolStripButton_Click(sender, e)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
Visible = true;
|
||||
@@ -1527,7 +1519,7 @@ namespace Netch.Forms
|
||||
|
||||
public void NotifyTip(string text, int timeout = 0, bool info = true)
|
||||
{
|
||||
// 会阻塞线程 timeout 秒
|
||||
// 会阻塞线程 timeout 秒(?)
|
||||
NotifyIcon.ShowBalloonTip(timeout, UpdateChecker.Name, text, info ? ToolTipIcon.Info : ToolTipIcon.Error);
|
||||
}
|
||||
|
||||
|
||||
132
Netch/Forms/MainForm.resx
Normal file
132
Netch/Forms/MainForm.resx
Normal file
@@ -0,0 +1,132 @@
|
||||
<?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>
|
||||
<metadata name="MenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="StatusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>130, 17</value>
|
||||
</metadata>
|
||||
<metadata name="NotifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>246, 17</value>
|
||||
</metadata>
|
||||
<metadata name="NotifyMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>359, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
123
Netch/Forms/Mode/Process.Designer.cs
generated
123
Netch/Forms/Mode/Process.Designer.cs
generated
@@ -31,25 +31,21 @@ namespace Netch.Forms.Mode
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.RemarkLabel = new System.Windows.Forms.Label();
|
||||
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
||||
this.FilenameLabel = new System.Windows.Forms.Label();
|
||||
this.FilenameTextBox = new System.Windows.Forms.TextBox();
|
||||
this.containerControl1 = new System.Windows.Forms.ContainerControl();
|
||||
this.RuleListBox = new System.Windows.Forms.ListBox();
|
||||
this.RuleRichTextBox = new System.Windows.Forms.RichTextBox();
|
||||
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.ProcessNameTextBox = new System.Windows.Forms.TextBox();
|
||||
this.AddButton = new System.Windows.Forms.Button();
|
||||
this.SelectButton = new System.Windows.Forms.Button();
|
||||
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ScanButton = new System.Windows.Forms.Button();
|
||||
this.ValidationButton = new System.Windows.Forms.Button();
|
||||
this.ControlButton = new System.Windows.Forms.Button();
|
||||
this.ConfigurationGroupBox.SuspendLayout();
|
||||
this.containerControl1.SuspendLayout();
|
||||
this.ProcessGroupBox.SuspendLayout();
|
||||
this.contextMenuStrip.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// ConfigurationGroupBox
|
||||
@@ -60,11 +56,12 @@ namespace Netch.Forms.Mode
|
||||
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.ProcessGroupBox);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.SelectButton);
|
||||
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 12);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.ControlButton);
|
||||
this.ConfigurationGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 5);
|
||||
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
|
||||
this.ConfigurationGroupBox.Size = new System.Drawing.Size(340, 344);
|
||||
this.ConfigurationGroupBox.TabIndex = 1;
|
||||
this.ConfigurationGroupBox.Size = new System.Drawing.Size(431, 378);
|
||||
this.ConfigurationGroupBox.TabIndex = 0;
|
||||
this.ConfigurationGroupBox.TabStop = false;
|
||||
this.ConfigurationGroupBox.Text = "Configuration";
|
||||
//
|
||||
@@ -81,7 +78,7 @@ namespace Netch.Forms.Mode
|
||||
//
|
||||
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
|
||||
this.RemarkTextBox.Name = "RemarkTextBox";
|
||||
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
|
||||
this.RemarkTextBox.Size = new System.Drawing.Size(341, 23);
|
||||
this.RemarkTextBox.TabIndex = 1;
|
||||
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
|
||||
//
|
||||
@@ -99,86 +96,76 @@ namespace Netch.Forms.Mode
|
||||
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
|
||||
this.FilenameTextBox.Name = "FilenameTextBox";
|
||||
this.FilenameTextBox.ReadOnly = true;
|
||||
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
|
||||
this.FilenameTextBox.Size = new System.Drawing.Size(341, 23);
|
||||
this.FilenameTextBox.TabIndex = 3;
|
||||
//
|
||||
// containerControl1
|
||||
//
|
||||
this.containerControl1.Controls.Add(this.RuleListBox);
|
||||
this.containerControl1.Controls.Add(this.RuleRichTextBox);
|
||||
this.containerControl1.Location = new System.Drawing.Point(6, 81);
|
||||
this.containerControl1.Name = "containerControl1";
|
||||
this.containerControl1.Size = new System.Drawing.Size(328, 176);
|
||||
this.containerControl1.TabIndex = 5;
|
||||
this.containerControl1.Size = new System.Drawing.Size(419, 221);
|
||||
this.containerControl1.TabIndex = 4;
|
||||
this.containerControl1.Text = "containerControl1";
|
||||
//
|
||||
// RuleListBox
|
||||
// RuleRichTextBox
|
||||
//
|
||||
this.RuleListBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.RuleListBox.FormattingEnabled = true;
|
||||
this.RuleListBox.ItemHeight = 17;
|
||||
this.RuleListBox.Location = new System.Drawing.Point(0, 0);
|
||||
this.RuleListBox.Name = "RuleListBox";
|
||||
this.RuleListBox.Size = new System.Drawing.Size(328, 176);
|
||||
this.RuleListBox.TabIndex = 0;
|
||||
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
|
||||
this.RuleRichTextBox.DetectUrls = false;
|
||||
this.RuleRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.RuleRichTextBox.Location = new System.Drawing.Point(0, 0);
|
||||
this.RuleRichTextBox.Name = "RuleRichTextBox";
|
||||
this.RuleRichTextBox.Size = new System.Drawing.Size(419, 221);
|
||||
this.RuleRichTextBox.TabIndex = 0;
|
||||
this.RuleRichTextBox.Text = "";
|
||||
this.RuleRichTextBox.WordWrap = false;
|
||||
//
|
||||
// ProcessGroupBox
|
||||
//
|
||||
this.ProcessGroupBox.Controls.Add(this.ProcessNameTextBox);
|
||||
this.ProcessGroupBox.Controls.Add(this.AddButton);
|
||||
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 263);
|
||||
this.ProcessGroupBox.Controls.Add(this.SelectButton);
|
||||
this.ProcessGroupBox.Controls.Add(this.ScanButton);
|
||||
this.ProcessGroupBox.Controls.Add(this.ValidationButton);
|
||||
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 295);
|
||||
this.ProcessGroupBox.Name = "ProcessGroupBox";
|
||||
this.ProcessGroupBox.Size = new System.Drawing.Size(328, 46);
|
||||
this.ProcessGroupBox.TabIndex = 6;
|
||||
this.ProcessGroupBox.Size = new System.Drawing.Size(419, 44);
|
||||
this.ProcessGroupBox.TabIndex = 5;
|
||||
this.ProcessGroupBox.TabStop = false;
|
||||
//
|
||||
// ProcessNameTextBox
|
||||
//
|
||||
this.ProcessNameTextBox.Location = new System.Drawing.Point(6, 15);
|
||||
this.ProcessNameTextBox.Name = "ProcessNameTextBox";
|
||||
this.ProcessNameTextBox.Size = new System.Drawing.Size(222, 23);
|
||||
this.ProcessNameTextBox.TabIndex = 0;
|
||||
//
|
||||
// AddButton
|
||||
//
|
||||
this.AddButton.Location = new System.Drawing.Point(247, 15);
|
||||
this.AddButton.Name = "AddButton";
|
||||
this.AddButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.AddButton.TabIndex = 1;
|
||||
this.AddButton.Text = "Add";
|
||||
this.AddButton.UseVisualStyleBackColor = true;
|
||||
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
|
||||
//
|
||||
// SelectButton
|
||||
//
|
||||
this.SelectButton.Location = new System.Drawing.Point(6, 315);
|
||||
this.SelectButton.Location = new System.Drawing.Point(6, 13);
|
||||
this.SelectButton.Name = "SelectButton";
|
||||
this.SelectButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.SelectButton.TabIndex = 7;
|
||||
this.SelectButton.TabIndex = 0;
|
||||
this.SelectButton.Text = "Select";
|
||||
this.SelectButton.UseVisualStyleBackColor = true;
|
||||
this.SelectButton.Click += new System.EventHandler(this.SelectButton_Click);
|
||||
//
|
||||
// contextMenuStrip
|
||||
// ScanButton
|
||||
//
|
||||
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.DeleteToolStripMenuItem});
|
||||
this.contextMenuStrip.Name = "contextMenuStrip";
|
||||
this.contextMenuStrip.Size = new System.Drawing.Size(114, 26);
|
||||
this.ScanButton.Location = new System.Drawing.Point(87, 13);
|
||||
this.ScanButton.Name = "ScanButton";
|
||||
this.ScanButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.ScanButton.TabIndex = 1;
|
||||
this.ScanButton.Text = "Scan";
|
||||
this.ScanButton.UseVisualStyleBackColor = true;
|
||||
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
|
||||
//
|
||||
// DeleteToolStripMenuItem
|
||||
// ValidationButton
|
||||
//
|
||||
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
|
||||
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(113, 22);
|
||||
this.DeleteToolStripMenuItem.Text = "Delete";
|
||||
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
|
||||
this.ValidationButton.Location = new System.Drawing.Point(338, 13);
|
||||
this.ValidationButton.Name = "ValidationButton";
|
||||
this.ValidationButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.ValidationButton.TabIndex = 2;
|
||||
this.ValidationButton.Text = "Validation";
|
||||
this.ValidationButton.UseVisualStyleBackColor = true;
|
||||
this.ValidationButton.Click += new System.EventHandler(this.ValidationButton_Click);
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Location = new System.Drawing.Point(277, 362);
|
||||
this.ControlButton.Location = new System.Drawing.Point(344, 345);
|
||||
this.ControlButton.Name = "ControlButton";
|
||||
this.ControlButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.ControlButton.TabIndex = 2;
|
||||
this.ControlButton.TabIndex = 6;
|
||||
this.ControlButton.Text = "Save";
|
||||
this.ControlButton.UseVisualStyleBackColor = true;
|
||||
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
|
||||
@@ -187,14 +174,14 @@ namespace Netch.Forms.Mode
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(364, 397);
|
||||
this.ClientSize = new System.Drawing.Size(455, 388);
|
||||
this.Controls.Add(this.ConfigurationGroupBox);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
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.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "Process";
|
||||
this.Padding = new System.Windows.Forms.Padding(12, 5, 12, 5);
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Create Process Mode";
|
||||
this.Load += new System.EventHandler(this.ModeForm_Load);
|
||||
@@ -202,27 +189,23 @@ namespace Netch.Forms.Mode
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
this.containerControl1.ResumeLayout(false);
|
||||
this.ProcessGroupBox.ResumeLayout(false);
|
||||
this.ProcessGroupBox.PerformLayout();
|
||||
this.contextMenuStrip.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button ScanButton;
|
||||
private System.Windows.Forms.ContainerControl containerControl1;
|
||||
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;
|
||||
private System.Windows.Forms.TextBox RemarkTextBox;
|
||||
private System.Windows.Forms.TextBox ProcessNameTextBox;
|
||||
private System.Windows.Forms.Button AddButton;
|
||||
private System.Windows.Forms.Button SelectButton;
|
||||
public System.Windows.Forms.Button ControlButton;
|
||||
private System.Windows.Forms.Label FilenameLabel;
|
||||
private System.Windows.Forms.TextBox FilenameTextBox;
|
||||
private RichTextBox RuleRichTextBox;
|
||||
private Button ValidationButton;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -33,10 +35,24 @@ namespace Netch.Forms.Mode
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否被编辑过
|
||||
/// </summary>
|
||||
public bool Edited { get; private set; }
|
||||
#region Model
|
||||
|
||||
public IEnumerable<string> Rules => RuleRichTextBox.Lines;
|
||||
|
||||
private void RuleAdd(string value)
|
||||
{
|
||||
RuleRichTextBox.AppendText($"{value}\n");
|
||||
}
|
||||
|
||||
private void RuleAddRange(IEnumerable<string> value)
|
||||
{
|
||||
foreach (string s in value)
|
||||
{
|
||||
RuleAdd(s);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void ModeForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
@@ -47,60 +63,10 @@ namespace Netch.Forms.Mode
|
||||
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
|
||||
RemarkTextBox.Text = _mode.Remark;
|
||||
FilenameTextBox.Text = _mode.RelativePath;
|
||||
RuleListBox.Items.AddRange(_mode.Rule.Cast<object>().ToArray());
|
||||
RuleAddRange(_mode.Rule);
|
||||
}
|
||||
|
||||
i18N.TranslateForm(this);
|
||||
i18N.Translate(contextMenuStrip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// listBox右键菜单
|
||||
/// </summary>
|
||||
private void RuleListBox_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y);
|
||||
if (RuleListBox.SelectedIndex == -1)
|
||||
return;
|
||||
|
||||
if (e.Button == MouseButtons.Right)
|
||||
contextMenuStrip.Show(RuleListBox, e.Location);
|
||||
}
|
||||
|
||||
private void deleteRule_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (RuleListBox.SelectedIndex == -1)
|
||||
return;
|
||||
|
||||
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
|
||||
Edited = true;
|
||||
}
|
||||
|
||||
private async void AddButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("rule can not be empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NFController.CheckCppRegex(ProcessNameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show("Rule does not conform to C++ regular expression syntax");
|
||||
return;
|
||||
}
|
||||
|
||||
var process = ProcessNameTextBox.Text;
|
||||
|
||||
if (!RuleListBox.Items.Contains(process))
|
||||
RuleListBox.Items.Add(process);
|
||||
|
||||
Edited = true;
|
||||
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
private void SelectButton_Click(object sender, EventArgs e)
|
||||
@@ -108,7 +74,7 @@ namespace Netch.Forms.Mode
|
||||
var dialog = new CommonOpenFileDialog
|
||||
{
|
||||
IsFolderPicker = true,
|
||||
Multiselect = false,
|
||||
Multiselect = true,
|
||||
Title = i18N.Translate("Select a folder"),
|
||||
AddToMostRecentlyUsedList = false,
|
||||
EnsurePathExists = true,
|
||||
@@ -117,17 +83,20 @@ namespace Netch.Forms.Mode
|
||||
|
||||
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
|
||||
{
|
||||
var path = dialog.FileName;
|
||||
if (!path.EndsWith(@"\"))
|
||||
path += @"\";
|
||||
foreach (string p in dialog.FileNames)
|
||||
{
|
||||
string path = p;
|
||||
if (!path.EndsWith(@"\"))
|
||||
path += @"\";
|
||||
|
||||
RuleListBox.Items.Add(path.ToRegexString());
|
||||
RuleAdd($"^{path.ToRegexString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (RuleListBox.Items.Count == 0)
|
||||
if (!RuleRichTextBox.Lines.Any())
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
return;
|
||||
@@ -149,11 +118,9 @@ namespace Netch.Forms.Mode
|
||||
{
|
||||
_mode.Remark = RemarkTextBox.Text;
|
||||
_mode.Rule.Clear();
|
||||
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
_mode.Rule.AddRange(RuleRichTextBox.Lines);
|
||||
|
||||
_mode.WriteFile();
|
||||
Global.MainForm.LoadModes();
|
||||
Edited = false;
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
}
|
||||
else
|
||||
@@ -173,10 +140,9 @@ namespace Netch.Forms.Mode
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
|
||||
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
mode.Rule.AddRange(RuleRichTextBox.Lines);
|
||||
|
||||
mode.WriteFile();
|
||||
ModeHelper.Add(mode);
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
}
|
||||
|
||||
@@ -190,5 +156,57 @@ namespace Netch.Forms.Mode
|
||||
FilenameTextBox.Text = FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text);
|
||||
}));
|
||||
}
|
||||
|
||||
private void ScanButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var dialog = new CommonOpenFileDialog
|
||||
{
|
||||
IsFolderPicker = true,
|
||||
Multiselect = false,
|
||||
Title = i18N.Translate("Select a folder"),
|
||||
AddToMostRecentlyUsedList = false,
|
||||
EnsurePathExists = true,
|
||||
NavigateToShortcut = true
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
|
||||
{
|
||||
var path = dialog.FileName;
|
||||
var list = new List<string>();
|
||||
const uint maxCount = 50;
|
||||
try
|
||||
{
|
||||
ScanDirectory(path, list);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate($"The number of executable files in the \"{path}\" directory is greater than {maxCount}"),
|
||||
LogLevel.WARNING);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RuleAddRange(list);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanDirectory(string directory, List<string> list, uint maxCount = 30)
|
||||
{
|
||||
foreach (string dir in Directory.GetDirectories(directory))
|
||||
ScanDirectory(dir, list, maxCount);
|
||||
|
||||
list.AddRange(Directory.GetFiles(directory).Select(Path.GetFileName).Where(s => s.EndsWith(".exe")).Select(s => s.ToRegexString()));
|
||||
|
||||
if (maxCount != 0 && list.Count > maxCount)
|
||||
throw new Exception("The number of results is greater than maxCount");
|
||||
}
|
||||
|
||||
private void ValidationButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (NFController.CheckRules(Rules, out var results))
|
||||
MessageBoxX.Show(NFController.GenerateInvalidRulesMessage(results), LogLevel.WARNING);
|
||||
else
|
||||
MessageBoxX.Show("Fine");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,6 @@ namespace Netch.Forms.Mode
|
||||
_mode.Type = (int) comboBox1.SelectedValue;
|
||||
|
||||
_mode.WriteFile();
|
||||
Global.MainForm.LoadModes();
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
}
|
||||
else
|
||||
@@ -105,7 +104,6 @@ namespace Netch.Forms.Mode
|
||||
mode.Rule.AddRange(richTextBox1.Lines);
|
||||
|
||||
mode.WriteFile();
|
||||
ModeHelper.Add(mode);
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace Netch.Forms
|
||||
private void SettingForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
|
||||
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Global.Flags.SupportFakeDns)));
|
||||
Task.Run(() => BeginInvoke(new Action(() => UseFakeDNSCheckBox.Visible = Flags.SupportFakeDns)));
|
||||
}
|
||||
|
||||
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object? sender, EventArgs? e)
|
||||
|
||||
@@ -2,10 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using WindowsJobAPI;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
|
||||
@@ -13,33 +10,11 @@ namespace Netch
|
||||
{
|
||||
public static class Global
|
||||
{
|
||||
/// <summary>
|
||||
/// 换行
|
||||
/// </summary>
|
||||
public const string EOF = "\r\n";
|
||||
|
||||
public const string UserACL = "data\\user.acl";
|
||||
public const string BuiltinACL = "bin\\default.acl";
|
||||
|
||||
public static readonly string NetchDir = Application.StartupPath;
|
||||
|
||||
public static readonly string NetchExecutable = Application.ExecutablePath;
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
private static readonly Lazy<MainForm> LazyMainForm = new(() => new MainForm());
|
||||
|
||||
private static readonly Lazy<Mutex> LazyMutex = new(() => new Mutex(false, "Global\\Netch"));
|
||||
|
||||
public static Mutex Mutex => LazyMutex.Value;
|
||||
|
||||
#if DEBUG
|
||||
public static bool Testing = false;
|
||||
#else
|
||||
public const bool Testing = false;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 用于读取和写入的配置
|
||||
/// </summary>
|
||||
@@ -50,20 +25,6 @@ namespace Netch
|
||||
/// </summary>
|
||||
public static readonly List<Mode> Modes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Windows Job API
|
||||
/// </summary>
|
||||
public static readonly JobObject Job = new();
|
||||
|
||||
public static class Flags
|
||||
{
|
||||
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
|
||||
|
||||
private static readonly Lazy<bool> LazySupportFakeDns = new(() => new TUNTAPController().TestFakeDNS());
|
||||
|
||||
public static bool SupportFakeDns => LazySupportFakeDns.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
@@ -75,5 +36,8 @@ namespace Netch
|
||||
IgnoreNullValues = true,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
};
|
||||
|
||||
public static readonly string NetchDir = Application.StartupPath;
|
||||
public static readonly string NetchExecutable = Application.ExecutablePath;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,28 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace Netch.Models.GitHubRelease
|
||||
{
|
||||
[Serializable]
|
||||
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
|
||||
public struct SuffixVersion : IComparable, IComparable<SuffixVersion>
|
||||
{
|
||||
public int Major { get; }
|
||||
public Version Version { get; }
|
||||
|
||||
public int Minor { get; }
|
||||
public string Suffix { 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)
|
||||
public SuffixVersion(Version version, string suffix)
|
||||
{
|
||||
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;
|
||||
Version = version;
|
||||
Suffix = suffix;
|
||||
}
|
||||
|
||||
public static SuffixVersion Parse(string input)
|
||||
{
|
||||
var splitStr = input.Split('-');
|
||||
var dotNetVersion = Version.Parse(splitStr[0]);
|
||||
var preRelease = new StringBuilder();
|
||||
var build = 0;
|
||||
var split = input.Split('-');
|
||||
var dotNetVersion = Version.Parse(split[0]);
|
||||
var preRelease = split.ElementAtOrDefault(1) ?? string.Empty;
|
||||
|
||||
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);
|
||||
return new SuffixVersion(dotNetVersion, preRelease);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out SuffixVersion result)
|
||||
@@ -65,17 +39,12 @@ namespace Netch.Models.GitHubRelease
|
||||
}
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new SuffixVersion(Major, Major, Patch, PreRelease, Build);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is SuffixVersion version)
|
||||
return CompareTo(version);
|
||||
if (obj is not SuffixVersion version)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
return -1;
|
||||
return CompareTo(version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,57 +55,27 @@ namespace Netch.Models.GitHubRelease
|
||||
/// </returns>
|
||||
public int CompareTo(SuffixVersion other)
|
||||
{
|
||||
var majorComparison = Major.CompareTo(other.Major);
|
||||
if (majorComparison != 0)
|
||||
return majorComparison;
|
||||
var versionComparison = Version.CompareTo(other.Version);
|
||||
if (versionComparison != 0)
|
||||
return versionComparison;
|
||||
|
||||
var minorComparison = Minor.CompareTo(other.Minor);
|
||||
if (minorComparison != 0)
|
||||
return minorComparison;
|
||||
if (Suffix == string.Empty)
|
||||
return other.Suffix == string.Empty ? 0 : 1;
|
||||
|
||||
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)
|
||||
if (other.Suffix == 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;
|
||||
}
|
||||
var suffixComparison = string.Compare(Suffix, other.Suffix, StringComparison.OrdinalIgnoreCase);
|
||||
return suffixComparison;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Major}.{Minor}.{Patch}{(string.IsNullOrEmpty(PreRelease) ? "" : "-")}{PreRelease}{(Build == 0 ? "" : Build.ToString())}";
|
||||
var s = Version.ToString();
|
||||
if (Suffix != string.Empty)
|
||||
s += $"-{Suffix}";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,41 @@ namespace Netch.Models
|
||||
{
|
||||
public class Mode
|
||||
{
|
||||
public readonly string? FullName;
|
||||
private readonly Lazy<List<string>> _lazyRule;
|
||||
|
||||
public string? FullName { get; private set; }
|
||||
|
||||
public Mode(string? fullName = default)
|
||||
{
|
||||
_lazyRule = new Lazy<List<string>>(ReadRules);
|
||||
if (fullName == null)
|
||||
return;
|
||||
|
||||
FullName = fullName;
|
||||
if (!File.Exists(FullName))
|
||||
return;
|
||||
|
||||
var text = File.ReadLines(FullName).First();
|
||||
|
||||
// load head
|
||||
if (text.First() != '#')
|
||||
throw new Exception($"mode {FullName} head not found at Line 0");
|
||||
|
||||
var split = text.Substring(1).SplitTrimEntries(',');
|
||||
Remark = split[0];
|
||||
|
||||
var typeResult = int.TryParse(split.ElementAtOrDefault(1), out var type);
|
||||
Type = typeResult ? type : 0;
|
||||
// TODO throw NotSupportedModeTypeException
|
||||
|
||||
var bypassChinaResult = int.TryParse(split.ElementAtOrDefault(2), out var bypassChina);
|
||||
BypassChina = this.ClientRouting() && bypassChinaResult && bypassChina == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 规则
|
||||
/// </summary>
|
||||
public readonly List<string> Rule = new();
|
||||
public List<string> Rule => _lazyRule.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 绕过中国(0. 不绕过 1. 绕过)
|
||||
@@ -45,15 +74,6 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public int Type { get; set; } = 0;
|
||||
|
||||
public Mode(string fullName)
|
||||
{
|
||||
FullName = fullName;
|
||||
}
|
||||
|
||||
public Mode()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件相对路径(必须是存在的文件)
|
||||
/// </summary>
|
||||
@@ -79,7 +99,7 @@ namespace Netch.Models
|
||||
relativePath.Replace(">", "");
|
||||
relativePath.Replace(".h", ".txt");
|
||||
|
||||
var mode = Global.Modes.FirstOrDefault(m => m!.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
|
||||
var mode = Global.Modes.FirstOrDefault(m => m.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
|
||||
|
||||
if (mode == null)
|
||||
throw new MessageException($"{relativePath} file included in {Remark} not found");
|
||||
@@ -105,6 +125,14 @@ namespace Netch.Models
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> ReadRules()
|
||||
{
|
||||
if (FullName == null || !File.Exists(FullName))
|
||||
return new List<string>();
|
||||
|
||||
return File.ReadLines(FullName!).Skip(1).ToList();
|
||||
}
|
||||
|
||||
public void WriteFile(string? fullName = null)
|
||||
{
|
||||
if (fullName != null)
|
||||
@@ -133,7 +161,7 @@ namespace Netch.Models
|
||||
/// <returns>模式文件字符串</returns>
|
||||
public string ToFileString()
|
||||
{
|
||||
return $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}{Global.EOF}{string.Join(Global.EOF, Rule)}";
|
||||
return $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}{Constants.EOF}{string.Join(Constants.EOF, Rule)}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
121
Netch/Models/ParameterBase.cs
Normal file
121
Netch/Models/ParameterBase.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public abstract class ParameterBase
|
||||
{
|
||||
// null value par
|
||||
|
||||
private readonly bool _full;
|
||||
|
||||
protected readonly string ParametersSeparate = " ";
|
||||
protected readonly string Separate = " ";
|
||||
protected readonly string VerbPrefix = "-";
|
||||
protected readonly string FullPrefix = "--";
|
||||
|
||||
protected ParameterBase()
|
||||
{
|
||||
_full = !GetType().IsDefined(typeof(VerbAttribute));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast<string>();
|
||||
return string.Join(ParametersSeparate, parameters).Trim();
|
||||
}
|
||||
|
||||
private string? PropToParameter(PropertyInfo p)
|
||||
{
|
||||
// prefix
|
||||
bool full;
|
||||
if (p.IsDefined(typeof(VerbAttribute)))
|
||||
full = false;
|
||||
else if (p.IsDefined(typeof(FullAttribute)))
|
||||
full = true;
|
||||
else
|
||||
full = _full;
|
||||
|
||||
var prefix = full ? FullPrefix : VerbPrefix;
|
||||
// key
|
||||
var key = p.GetCustomAttribute<RealNameAttribute>()?.Name ?? p.Name;
|
||||
|
||||
// build
|
||||
var value = p.GetValue(this);
|
||||
switch (value)
|
||||
{
|
||||
case bool b:
|
||||
return b ? $"{prefix}{key}" : null;
|
||||
default:
|
||||
if ((value?.ToString() ?? null).IsNullOrWhiteSpace())
|
||||
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
|
||||
|
||||
if (p.IsDefined(typeof(QuoteAttribute)))
|
||||
value = $"\"{value}\"";
|
||||
|
||||
return $"{prefix}{key}{Separate}{value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
|
||||
public class VerbAttribute : Attribute
|
||||
{
|
||||
// Don't use verb and full both on one class or property
|
||||
// if you did, will take verb
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
|
||||
public class FullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OptionalAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class QuoteAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class RealNameAttribute : Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public RealNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RequiredArgumentValueInvalidException : Exception
|
||||
{
|
||||
public string? ArgumentName { get; }
|
||||
|
||||
public object? ArgumentObject { get; }
|
||||
|
||||
private readonly string? _message;
|
||||
|
||||
private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty.";
|
||||
|
||||
public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName);
|
||||
|
||||
public RequiredArgumentValueInvalidException()
|
||||
{
|
||||
_message = "Some Argument value invalid. A required argument value's can't be null or empty.";
|
||||
}
|
||||
|
||||
public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message)
|
||||
{
|
||||
ArgumentName = argumentName;
|
||||
ArgumentObject = argumentObject;
|
||||
_message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,35 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.User32;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
public static class Netch
|
||||
{
|
||||
public static readonly SingleInstance.SingleInstance SingleInstance = new($"Global\\{nameof(Netch)}");
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Contains("-console"))
|
||||
if (!NativeMethods.AttachConsole(-1))
|
||||
NativeMethods.AllocConsole();
|
||||
#if DEBUG
|
||||
AttachConsole();
|
||||
#else
|
||||
if (args.Contains(Constants.Parameter.Console))
|
||||
AttachConsole();
|
||||
#endif
|
||||
|
||||
if (args.Contains(Constants.Parameter.ForceUpdate))
|
||||
Flags.AlwaysShowNewVersionFound = true;
|
||||
|
||||
// 设置当前目录
|
||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||
@@ -43,15 +48,16 @@ namespace Netch
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!Global.Mutex.WaitOne(0, false))
|
||||
if (!SingleInstance.IsFirstInstance)
|
||||
{
|
||||
ShowOpened();
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
SingleInstance.PassArgumentsToFirstInstance(args.Append(Constants.Parameter.Show));
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
SingleInstance.ArgumentsReceived.Subscribe(SingleInstance_ArgumentsReceived);
|
||||
SingleInstance.ListenForArgumentsFromSuccessiveInstances();
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
{
|
||||
@@ -85,46 +91,24 @@ namespace Netch
|
||||
Application.Run(Global.MainForm);
|
||||
}
|
||||
|
||||
private static void AttachConsole()
|
||||
{
|
||||
if (!NativeMethods.AttachConsole(-1))
|
||||
NativeMethods.AllocConsole();
|
||||
}
|
||||
|
||||
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
Logging.Error(e.Exception.ToString());
|
||||
Utils.Utils.Open(Logging.LogFile);
|
||||
}
|
||||
|
||||
private static void ShowOpened()
|
||||
private static void SingleInstance_ArgumentsReceived(IEnumerable<string> args)
|
||||
{
|
||||
HWND GetWindowHandleByPidAndTitle(int process, string title)
|
||||
if (args.Contains(Constants.Parameter.Show))
|
||||
{
|
||||
var sb = new StringBuilder(256);
|
||||
HWND pLast = IntPtr.Zero;
|
||||
do
|
||||
{
|
||||
pLast = FindWindowEx(HWND.NULL, pLast, null, null);
|
||||
GetWindowThreadProcessId(pLast, out var id);
|
||||
if (id != process)
|
||||
continue;
|
||||
|
||||
if (GetWindowText(pLast, sb, sb.Capacity) <= 0)
|
||||
continue;
|
||||
|
||||
if (sb.ToString().Equals(title))
|
||||
return pLast;
|
||||
} while (pLast != IntPtr.Zero);
|
||||
|
||||
return HWND.NULL;
|
||||
Global.MainForm.ShowMainFormToolStripButton_Click(null!, null!);
|
||||
}
|
||||
|
||||
var self = Process.GetCurrentProcess();
|
||||
var activeProcess = Process.GetProcessesByName("Netch").Single(p => p.Id != self.Id);
|
||||
HWND handle = activeProcess.MainWindowHandle;
|
||||
if (handle.IsNull)
|
||||
handle = GetWindowHandleByPidAndTitle(activeProcess.Id, "Netch");
|
||||
|
||||
if (handle.IsNull)
|
||||
return;
|
||||
|
||||
ShowWindow(handle, ShowWindowCommand.SW_NORMAL);
|
||||
SwitchToThisWindow(handle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- <EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@@ -40,18 +43,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.0" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.5" />
|
||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.7" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.5" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.7" />
|
||||
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
|
||||
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
|
||||
<PackageReference Include="WindowsProxy" Version="5.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -71,6 +78,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Interop.nfapinet\Interop.nfapinet.csproj" />
|
||||
<ProjectReference Include="..\SearchComboBox\SearchComboBox.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -117,7 +125,7 @@
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="set TargetFramework=$(TargetFramework)
set Configuration=$(Configuration)
set ILMergeConsolePath=$(ILMergeConsolePath)
set TargetDir=$(TargetDir)
set SolutionDir=$(SolutionDir)
$(ProjectDir)PostBuild.bat" />
|
||||
<Exec Command="set TargetFramework=$(TargetFramework)
set Configuration=$(Configuration)
set ILMergeConsolePath=$(ILMergeConsolePath)
set TargetDir=$(TargetDir)
set SolutionDir=$(SolutionDir)
call $(ProjectDir)PostBuild.bat" />
|
||||
</Target>
|
||||
|
||||
<Target Condition="'$(PublishSingleFile)' == 'true'" AfterTargets="_ComputeFilesToBundle" Name="RemoveDupeAssemblies">
|
||||
|
||||
64
Netch/Properties/Resources.Designer.cs
generated
64
Netch/Properties/Resources.Designer.cs
generated
@@ -1,10 +1,10 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本: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>
|
||||
// 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")]
|
||||
// 此类是由 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 {
|
||||
@@ -33,7 +33,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </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>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
@@ -61,7 +61,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static byte[] _7za {
|
||||
get {
|
||||
@@ -71,17 +71,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] abp_js {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("abp_js", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap CopyLink {
|
||||
get {
|
||||
@@ -91,17 +81,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] defaultTUNTAP {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("defaultTUNTAP", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap delete {
|
||||
get {
|
||||
@@ -111,7 +91,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap edit {
|
||||
get {
|
||||
@@ -121,7 +101,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Icon icon {
|
||||
get {
|
||||
@@ -131,7 +111,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Netch {
|
||||
get {
|
||||
@@ -141,7 +121,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap speed {
|
||||
get {
|
||||
@@ -151,7 +131,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Sponsor {
|
||||
get {
|
||||
@@ -161,7 +141,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static byte[] zh_CN {
|
||||
get {
|
||||
|
||||
@@ -118,10 +118,6 @@
|
||||
<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="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089</value>
|
||||
@@ -150,10 +146,6 @@
|
||||
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<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>
|
||||
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
[Generic]
|
||||
Address = 10.0.236.10
|
||||
Netmask = 255.255.255.0
|
||||
Gateway = 10.0.236.1
|
||||
DNS = 1.1.1.1
|
||||
UseCustomDNS = False
|
||||
@@ -1,4 +1,5 @@
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.Shadowsocks.Form
|
||||
{
|
||||
@@ -8,7 +9,7 @@ namespace Netch.Servers.Shadowsocks.Form
|
||||
{
|
||||
server ??= new Shadowsocks();
|
||||
Server = server;
|
||||
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
|
||||
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
|
||||
CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
|
||||
CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin);
|
||||
CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption);
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "Shadowsocks.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"listening at"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"listening at"};
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"};
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"};
|
||||
|
||||
public override string Name { get; } = "Shadowsocks";
|
||||
|
||||
@@ -24,26 +24,60 @@ namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
var server = (Shadowsocks) s;
|
||||
|
||||
#region Argument
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($"-s {server.AutoResolveHostname()} " + $"-p {server.Port} " + $"-b {this.LocalAddress()} " +
|
||||
$"-l {this.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}\"");
|
||||
var command = new SSParameter
|
||||
{
|
||||
s = server.AutoResolveHostname(),
|
||||
p = server.Port,
|
||||
b = this.LocalAddress(),
|
||||
l = this.Socks5LocalPort(),
|
||||
m = server.EncryptMethod,
|
||||
k = server.Password,
|
||||
u = true,
|
||||
plugin = server.Plugin,
|
||||
plugin_opts = server.PluginOption
|
||||
};
|
||||
|
||||
if (mode.BypassChina)
|
||||
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
|
||||
command.acl = $"{Path.GetFullPath(File.Exists(Constants.UserACL) ? Constants.UserACL : Constants.BuiltinACL)}";
|
||||
|
||||
#endregion
|
||||
StartInstanceAuto(command.ToString());
|
||||
}
|
||||
|
||||
StartInstanceAuto(argument.ToString());
|
||||
[Verb]
|
||||
private class SSParameter : ParameterBase
|
||||
{
|
||||
public string? s { get; set; }
|
||||
|
||||
public ushort? p { get; set; }
|
||||
|
||||
public string? b { get; set; }
|
||||
|
||||
public ushort? l { get; set; }
|
||||
|
||||
public string? m { get; set; }
|
||||
|
||||
public string? k { get; set; }
|
||||
|
||||
public bool u { get; set; }
|
||||
|
||||
[Full]
|
||||
[Optional]
|
||||
public string? plugin { get; set; }
|
||||
|
||||
[Full]
|
||||
[Optional]
|
||||
[RealName("plugin-opts")]
|
||||
public string? plugin_opts { get; set; }
|
||||
|
||||
[Full]
|
||||
[Quote]
|
||||
[Optional]
|
||||
public string? acl { get; set; }
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
StopInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Servers.Shadowsocks
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string? Password { get; set; }
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 插件
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.ShadowsocksR.Form
|
||||
{
|
||||
@@ -8,7 +9,7 @@ namespace Netch.Servers.ShadowsocksR.Form
|
||||
{
|
||||
server ??= new ShadowsocksR();
|
||||
Server = server;
|
||||
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
|
||||
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
|
||||
CreateComboBox("EncryptMethod", "Encrypt Method", SSRGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
|
||||
CreateComboBox("Protocol", "Protocol", SSRGlobal.Protocols, s => server.Protocol = s, server.Protocol);
|
||||
CreateTextBox("ProtocolParam", "Protocol Param", s => true, s => server.ProtocolParam = s, server.ProtocolParam);
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "ShadowsocksR.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"listening at"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"listening at"};
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"Invalid config path", "usage"};
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"Invalid config path", "usage"};
|
||||
|
||||
public override string Name { get; } = "ShadowsocksR";
|
||||
|
||||
@@ -24,31 +24,64 @@ namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
var server = (ShadowsocksR) s;
|
||||
|
||||
#region Argument
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($"-s {server.AutoResolveHostname()} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120");
|
||||
if (!string.IsNullOrEmpty(server.Protocol))
|
||||
var command = new SSRParameter
|
||||
{
|
||||
argument.Append($" -O {server.Protocol}");
|
||||
if (!string.IsNullOrEmpty(server.ProtocolParam))
|
||||
argument.Append($" -G \"{server.ProtocolParam}\"");
|
||||
}
|
||||
s = server.AutoResolveHostname(),
|
||||
p = server.Port,
|
||||
k = server.Password,
|
||||
m = server.EncryptMethod,
|
||||
t = "120",
|
||||
O = server.Protocol,
|
||||
G = server.ProtocolParam,
|
||||
o = server.OBFS,
|
||||
g = server.OBFSParam,
|
||||
b = this.LocalAddress(),
|
||||
l = this.Socks5LocalPort(),
|
||||
u = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(server.OBFS))
|
||||
{
|
||||
argument.Append($" -o {server.OBFS}");
|
||||
if (!string.IsNullOrEmpty(server.OBFSParam))
|
||||
argument.Append($" -g \"{server.OBFSParam}\"");
|
||||
}
|
||||
|
||||
argument.Append($" -b {this.LocalAddress()} -l {this.Socks5LocalPort()} -u");
|
||||
if (mode.BypassChina)
|
||||
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
|
||||
command.acl = $"{Path.GetFullPath(File.Exists(Constants.UserACL) ? Constants.UserACL : Constants.BuiltinACL)}";
|
||||
|
||||
#endregion
|
||||
StartInstanceAuto(command.ToString());
|
||||
}
|
||||
|
||||
StartInstanceAuto(argument.ToString());
|
||||
[Verb]
|
||||
class SSRParameter : ParameterBase
|
||||
{
|
||||
public string? s { get; set; }
|
||||
|
||||
public ushort? p { get; set; }
|
||||
|
||||
[Quote]
|
||||
public string? k { get; set; }
|
||||
|
||||
public string? m { get; set; }
|
||||
|
||||
public string? t { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? O { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? G { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? o { get; set; }
|
||||
|
||||
[Optional]
|
||||
public string? g { get; set; }
|
||||
|
||||
public string? b { get; set; }
|
||||
|
||||
public ushort? l { get; set; }
|
||||
|
||||
public bool u { get; set; }
|
||||
|
||||
[Full]
|
||||
[Quote]
|
||||
[Optional]
|
||||
public string? acl { get; set; }
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Netch.Servers.ShadowsocksR
|
||||
// https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme
|
||||
// ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
|
||||
var paraStr =
|
||||
$"/?obfsparam={ShareLink.URLSafeBase64Encode(server.OBFSParam)}&protoparam={ShareLink.URLSafeBase64Encode(server.ProtocolParam)}&remarks={ShareLink.URLSafeBase64Encode(server.Remark)}";
|
||||
$"/?obfsparam={ShareLink.URLSafeBase64Encode(server.OBFSParam ?? "")}&protoparam={ShareLink.URLSafeBase64Encode(server.ProtocolParam ?? "")}&remarks={ShareLink.URLSafeBase64Encode(server.Remark)}";
|
||||
|
||||
return "ssr://" +
|
||||
ShareLink.URLSafeBase64Encode(
|
||||
|
||||
@@ -7,26 +7,16 @@ namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
public override string Type { get; } = "SSR";
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
/// </summary>
|
||||
public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
|
||||
|
||||
/// <summary>
|
||||
/// 混淆
|
||||
/// </summary>
|
||||
public string OBFS { get; set; } = SSRGlobal.OBFSs[0];
|
||||
|
||||
/// <summary>
|
||||
/// 混淆参数
|
||||
/// </summary>
|
||||
public string? OBFSParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
/// </summary>
|
||||
public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
|
||||
|
||||
/// <summary>
|
||||
/// 协议
|
||||
/// </summary>
|
||||
@@ -36,6 +26,16 @@ namespace Netch.Servers.ShadowsocksR
|
||||
/// 协议参数
|
||||
/// </summary>
|
||||
public string? ProtocolParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 混淆
|
||||
/// </summary>
|
||||
public string OBFS { get; set; } = SSRGlobal.OBFSs[0];
|
||||
|
||||
/// <summary>
|
||||
/// 混淆参数
|
||||
/// </summary>
|
||||
public string? OBFSParam { get; set; }
|
||||
}
|
||||
|
||||
public class SSRGlobal
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace Netch.Servers.Trojan
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "Trojan.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"started"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"started"};
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"exiting"};
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"exiting"};
|
||||
|
||||
public override string Name { get; } = "Trojan";
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/// <summary>
|
||||
/// 额外 ID
|
||||
/// </summary>
|
||||
public string aid { get; set; } = string.Empty;
|
||||
public int aid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 伪装域名(HTTP,WS)
|
||||
@@ -38,7 +38,7 @@
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public string port { get; set; } = string.Empty;
|
||||
public ushort port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
@@ -58,6 +58,6 @@
|
||||
/// <summary>
|
||||
/// 链接版本
|
||||
/// </summary>
|
||||
public string v { get; set; } = string.Empty;
|
||||
public int v { get; set; } = 2;
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace Netch.Servers.V2ray.Utils
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
if (Global.Flags.SupportFakeDns && Global.Settings.TUNTAP.UseFakeDNS)
|
||||
if (Flags.SupportFakeDns && Global.Settings.TUNTAP.UseFakeDNS)
|
||||
directRuleObject.domain.Add("geosite:cn");
|
||||
else
|
||||
directRuleObject.ip.Add("geoip:cn");
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Netch.Servers.V2ray
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "xray.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"started"};
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] {"started"};
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; } = new[] {"config file not readable", "failed to"};
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] {"config file not readable", "failed to"};
|
||||
|
||||
public override string Name { get; } = "Xray";
|
||||
|
||||
|
||||
@@ -77,9 +77,7 @@ namespace Netch.Servers.V2ray
|
||||
var parameter = new Dictionary<string, string>();
|
||||
// protocol-specific fields
|
||||
parameter.Add("type", server.TransferProtocol);
|
||||
if (server.EncryptMethod == "none")
|
||||
// VLESS outbounds[].settings.encryption,当前可选值只有 none
|
||||
parameter.Add("encryption", server.EncryptMethod);
|
||||
parameter.Add("encryption", server.EncryptMethod);
|
||||
|
||||
// transport-specific fields
|
||||
switch (server.TransferProtocol)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray;
|
||||
@@ -43,12 +44,12 @@ namespace Netch.Servers.VMess
|
||||
|
||||
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
|
||||
{
|
||||
v = "2",
|
||||
v = 2,
|
||||
ps = server.Remark,
|
||||
add = server.Hostname,
|
||||
port = server.Port.ToString(),
|
||||
port = server.Port,
|
||||
id = server.UserID,
|
||||
aid = server.AlterID.ToString(),
|
||||
aid = server.AlterID,
|
||||
net = server.TransferProtocol,
|
||||
type = server.FakeType,
|
||||
host = server.Host,
|
||||
@@ -85,13 +86,14 @@ namespace Netch.Servers.VMess
|
||||
return V2rayUtils.ParseVUri(text);
|
||||
}
|
||||
|
||||
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s)!;
|
||||
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s,
|
||||
new JsonSerializerOptions {NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString})!;
|
||||
|
||||
data.Remark = vmess.ps;
|
||||
data.Hostname = vmess.add;
|
||||
data.Port = ushort.Parse(vmess.port);
|
||||
data.Port = vmess.port;
|
||||
data.UserID = vmess.id;
|
||||
data.AlterID = int.Parse(vmess.aid);
|
||||
data.AlterID = vmess.aid;
|
||||
data.TransferProtocol = vmess.net;
|
||||
data.FakeType = vmess.type;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Windows.Threading;
|
||||
using Netch.Controllers;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
@@ -95,6 +96,28 @@ namespace Netch.Updater
|
||||
|
||||
private void ApplyUpdate()
|
||||
{
|
||||
var dispatcher = Dispatcher.CurrentDispatcher;
|
||||
var mainForm = Global.MainForm;
|
||||
|
||||
#region PreUpdate
|
||||
|
||||
ModeHelper.SuspendWatcher = true;
|
||||
// Stop and Save
|
||||
mainForm.Stop();
|
||||
Configuration.Save();
|
||||
|
||||
// Backup Configuration file
|
||||
try
|
||||
{
|
||||
File.Copy(Configuration.SettingFileFullName, Configuration.SettingFileFullName + ".bak", true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// extract Update file to {tempDirectory}\extract
|
||||
var extractPath = Path.Combine(_tempDirectory, "extract");
|
||||
int exitCode;
|
||||
@@ -107,11 +130,10 @@ namespace Netch.Updater
|
||||
// move {tempDirectory}\extract\Netch to install folder
|
||||
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _installDirectory);
|
||||
|
||||
// save, release mutex, then exit
|
||||
Configuration.Save();
|
||||
Global.MainForm.Invoke(new Action(() => { Global.Mutex.ReleaseMutex(); }));
|
||||
// release mutex, exit
|
||||
dispatcher.Invoke(Netch.SingleInstance.Dispose);
|
||||
Process.Start(Global.NetchExecutable);
|
||||
Global.MainForm.Exit(true, false);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private void MarkFilesOld()
|
||||
@@ -126,7 +148,7 @@ namespace Netch.Updater
|
||||
if (extendedKeepDirectories.Any(p => file.StartsWith(p)))
|
||||
continue;
|
||||
|
||||
if (Path.GetFileName(file) is ModeHelper.DISABLE_MODE_DIRECTORY_FILENAME)
|
||||
if (Path.GetFileName(file) is ModeHelper.DisableModeDirectoryFileName)
|
||||
continue;
|
||||
|
||||
filesToDelete.Add(file);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void NetTraffic()
|
||||
{
|
||||
if (!Global.Flags.IsWindows10Upper)
|
||||
if (!Flags.IsWindows10Upper)
|
||||
return;
|
||||
|
||||
var counterLock = new object();
|
||||
|
||||
@@ -12,12 +12,10 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 数据目录
|
||||
/// </summary>
|
||||
public const string DATA_DIR = "data";
|
||||
public static string DataDirectoryFullName => Path.Combine(Global.NetchDir, "data");
|
||||
|
||||
public static string SettingFileFullName => $"{DataDirectoryFullName}\\settings.json";
|
||||
|
||||
/// <summary>
|
||||
/// 设置
|
||||
/// </summary>
|
||||
public static readonly string SETTINGS_JSON = $"{DATA_DIR}\\settings.json";
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
|
||||
|
||||
static Configuration()
|
||||
@@ -31,45 +29,39 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void Load()
|
||||
{
|
||||
if (File.Exists(SETTINGS_JSON))
|
||||
if (File.Exists(SettingFileFullName))
|
||||
{
|
||||
Global.Settings = ParseSetting(File.ReadAllText(SETTINGS_JSON));
|
||||
try
|
||||
{
|
||||
using var fileStream = File.OpenRead(SettingFileFullName);
|
||||
var settings = JsonSerializer.DeserializeAsync<Setting>(fileStream, JsonSerializerOptions).Result!;
|
||||
|
||||
CheckSetting(settings);
|
||||
|
||||
Global.Settings = settings;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error(e.ToString());
|
||||
Utils.Open(Logging.LogFile);
|
||||
Environment.Exit(-1);
|
||||
Global.Settings = null!;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 弹出提示
|
||||
i18N.Load("System");
|
||||
|
||||
// 创建 data 文件夹并保存默认设置
|
||||
// 保存默认设置
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public static Setting ParseSetting(string text)
|
||||
private static void CheckSetting(Setting settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = JsonSerializer.Deserialize<Setting>(text, JsonSerializerOptions)!;
|
||||
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
|
||||
|
||||
#region Check Profile
|
||||
|
||||
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
|
||||
|
||||
if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index)))
|
||||
for (var i = 0; i < settings.Profiles.Count; i++)
|
||||
settings.Profiles[i].Index = i;
|
||||
|
||||
#endregion
|
||||
|
||||
return settings;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error(e.ToString());
|
||||
Utils.Open(Logging.LogFile);
|
||||
Environment.Exit(-1);
|
||||
return null!;
|
||||
}
|
||||
if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index)))
|
||||
for (var i = 0; i < settings.Profiles.Count; i++)
|
||||
settings.Profiles[i].Index = i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,10 +69,10 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void Save()
|
||||
{
|
||||
if (!Directory.Exists(DATA_DIR))
|
||||
Directory.CreateDirectory(DATA_DIR);
|
||||
if (!Directory.Exists(DataDirectoryFullName))
|
||||
Directory.CreateDirectory(DataDirectoryFullName);
|
||||
|
||||
File.WriteAllBytes(SETTINGS_JSON, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions));
|
||||
File.WriteAllBytes(SettingFileFullName, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,15 +39,24 @@ namespace Netch.Utils
|
||||
|
||||
private static void Write(string text, LogLevel logLevel)
|
||||
{
|
||||
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}";
|
||||
if (Global.Testing)
|
||||
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
|
||||
#if DEBUG
|
||||
switch (logLevel)
|
||||
{
|
||||
Console.WriteLine(contents);
|
||||
return;
|
||||
case LogLevel.INFO:
|
||||
case LogLevel.WARNING:
|
||||
Console.Write(contents);
|
||||
break;
|
||||
case LogLevel.ERROR:
|
||||
Console.Error.Write(contents);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
|
||||
}
|
||||
|
||||
#else
|
||||
lock (FileLock)
|
||||
File.AppendAllText(LogFile, contents);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,50 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class ModeHelper
|
||||
{
|
||||
private const string MODE_DIR = "mode";
|
||||
public const string DISABLE_MODE_DIRECTORY_FILENAME = "disabled";
|
||||
public const string DisableModeDirectoryFileName = "disabled";
|
||||
|
||||
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
|
||||
public static string ModeDirectoryFullName => Path.Combine(Global.NetchDir, "mode");
|
||||
|
||||
private static readonly FileSystemWatcher FileSystemWatcher;
|
||||
|
||||
public static bool SuspendWatcher { get; set; } = false;
|
||||
|
||||
static ModeHelper()
|
||||
{
|
||||
FileSystemWatcher = new FileSystemWatcher(ModeDirectoryFullName)
|
||||
{
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName,
|
||||
IncludeSubdirectories = true,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
FileSystemWatcher.Changed += OnModeChanged;
|
||||
FileSystemWatcher.Created += OnModeChanged;
|
||||
FileSystemWatcher.Deleted += OnModeChanged;
|
||||
FileSystemWatcher.Renamed += OnModeChanged;
|
||||
}
|
||||
|
||||
private static void OnModeChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (SuspendWatcher)
|
||||
return;
|
||||
|
||||
Load();
|
||||
Global.MainForm.LoadModes();
|
||||
}
|
||||
|
||||
public static string GetRelativePath(string fullName)
|
||||
{
|
||||
return fullName.Substring(ModeDirectory.Length);
|
||||
var length = ModeDirectoryFullName.Length;
|
||||
if (!ModeDirectoryFullName.EndsWith("\\"))
|
||||
length++;
|
||||
|
||||
return fullName.Substring(length);
|
||||
}
|
||||
|
||||
public static string GetFullPath(string relativeName)
|
||||
{
|
||||
return Path.Combine(ModeDirectory, relativeName);
|
||||
return Path.Combine(ModeDirectoryFullName, relativeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,7 +62,7 @@ namespace Netch.Utils
|
||||
public static void Load()
|
||||
{
|
||||
Global.Modes.Clear();
|
||||
LoadModeDirectory(ModeDirectory);
|
||||
LoadModeDirectory(ModeDirectoryFullName);
|
||||
|
||||
Sort();
|
||||
}
|
||||
@@ -44,11 +75,18 @@ namespace Netch.Utils
|
||||
LoadModeDirectory(directory);
|
||||
|
||||
// skip Directory with a disabled file in
|
||||
if (File.Exists(Path.Combine(modeDirectory, DISABLE_MODE_DIRECTORY_FILENAME)))
|
||||
if (File.Exists(Path.Combine(modeDirectory, DisableModeDirectoryFileName)))
|
||||
return;
|
||||
|
||||
foreach (var file in Directory.GetFiles(modeDirectory).Where(f => f.EndsWith(".txt")))
|
||||
LoadModeFile(file);
|
||||
try
|
||||
{
|
||||
Global.Modes.Add(new Mode(file));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -56,71 +94,17 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadModeFile(string fullName)
|
||||
{
|
||||
var mode = new Mode(fullName);
|
||||
|
||||
var content = File.ReadAllLines(fullName);
|
||||
if (content.Length == 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < content.Length; i++)
|
||||
{
|
||||
var text = content[i].Trim();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
if (text.First() != '#')
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var splited = text.Substring(1).SplitTrimEntries(',');
|
||||
|
||||
mode.Remark = splited[0];
|
||||
|
||||
var typeResult = int.TryParse(splited.ElementAtOrDefault(1), out var type);
|
||||
mode.Type = typeResult ? type : 0;
|
||||
|
||||
var bypassChinaResult = int.TryParse(splited.ElementAtOrDefault(2), out var bypassChina);
|
||||
mode.BypassChina = mode.ClientRouting() && bypassChinaResult && bypassChina == 1;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mode.Rule.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
Global.Modes.Add(mode);
|
||||
}
|
||||
|
||||
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.LoadModes();
|
||||
}
|
||||
|
||||
public static void Delete(Mode mode)
|
||||
{
|
||||
if (mode.FullName == null)
|
||||
throw new ArgumentException("FullName");
|
||||
throw new ArgumentException(nameof(mode.FullName));
|
||||
|
||||
if (File.Exists(mode.FullName))
|
||||
File.Delete(mode.FullName);
|
||||
|
||||
Global.Modes.Remove(mode);
|
||||
Global.MainForm.LoadModes();
|
||||
File.Delete(mode.FullName);
|
||||
}
|
||||
|
||||
public static bool SkipServerController(Server server, Mode mode)
|
||||
@@ -137,17 +121,15 @@ namespace Netch.Utils
|
||||
};
|
||||
}
|
||||
|
||||
public static IModeController? GetModeControllerByType(int type, out ushort? port, out string portName, out PortType portType)
|
||||
public static IModeController? GetModeControllerByType(int type, out ushort? port, out string portName)
|
||||
{
|
||||
port = null;
|
||||
portName = string.Empty;
|
||||
portType = PortType.Both;
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
port = Global.Settings.RedirectorTCPPort;
|
||||
portName = "Redirector TCP";
|
||||
portType = PortType.TCP;
|
||||
return new NFController();
|
||||
case 1:
|
||||
case 2:
|
||||
@@ -156,7 +138,6 @@ namespace Netch.Utils
|
||||
case 5:
|
||||
port = Global.Settings.HTTPLocalPort;
|
||||
portName = "HTTP";
|
||||
portType = PortType.TCP;
|
||||
StatusPortInfoText.HttpPort = (ushort) port;
|
||||
return new HTTPController();
|
||||
case 4:
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Netch.Models;
|
||||
using static Vanara.PInvoke.IpHlpApi;
|
||||
using static Vanara.PInvoke.Ws2_32;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -26,6 +28,16 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Process> GetProcessByUsedTcpPort(ushort port)
|
||||
{
|
||||
if (port == 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
var row = GetTcpTable2().Where(r => ntohs((ushort) r.dwLocalPort) == port).Where(r => r.dwOwningPid is not (0 or 4));
|
||||
|
||||
return row.Select(r => Process.GetProcessById((int) r.dwOwningPid));
|
||||
}
|
||||
|
||||
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
|
||||
{
|
||||
var process = new Process
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Netch.Utils
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Unsupported Server Type
|
||||
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace Netch.Utils
|
||||
{
|
||||
public static bool Open(string path)
|
||||
{
|
||||
if (Global.Testing)
|
||||
return true;
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
@@ -116,30 +114,11 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static void KillProcessByName(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var p in Process.GetProcessesByName(name))
|
||||
if (p.MainModule != null && p.MainModule.FileName.StartsWith(Global.NetchDir))
|
||||
p.Kill();
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
Logging.Error($"结束进程 {name} 错误:" + e.Message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFileVersion(string file)
|
||||
{
|
||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
|
||||
}
|
||||
|
||||
|
||||
public static void DrawCenterComboBox(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
if (sender is ComboBox cbx)
|
||||
|
||||
3
SearchComboBox/.gitignore
vendored
3
SearchComboBox/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
/bin
|
||||
/obj
|
||||
/SearchComboBox.csproj.user
|
||||
3
UnitTest/.gitignore
vendored
3
UnitTest/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
/bin
|
||||
/obj
|
||||
/Netch.csproj.user
|
||||
@@ -6,10 +6,10 @@ using Netch.Utils;
|
||||
namespace UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class FunctionTest : TestBase
|
||||
public class Function : TestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestLoadI18N()
|
||||
public void LoadLanguage()
|
||||
{
|
||||
void TestLoad(string t)
|
||||
{
|
||||
98
UnitTest/ParameterTest.cs
Normal file
98
UnitTest/ParameterTest.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Netch.Models;
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class ParameterTest
|
||||
{
|
||||
[Verb]
|
||||
private class VerbAndRealName : ParameterBase
|
||||
{
|
||||
[RealName("v")]
|
||||
public string v1 { get; } = "a";
|
||||
|
||||
[RealName("v")]
|
||||
public string v2 { get; } = "b";
|
||||
}
|
||||
|
||||
private class Full : ParameterBase
|
||||
{
|
||||
[RealName("f")]
|
||||
public string f1 { get; } = "a";
|
||||
|
||||
[RealName("f")]
|
||||
public string f2 { get; } = "b";
|
||||
}
|
||||
|
||||
private class FullWithVerb : ParameterBase
|
||||
{
|
||||
public string f { get; } = "a";
|
||||
|
||||
[Verb]
|
||||
public string v { get; } = "b";
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private class VerbWithFull : ParameterBase
|
||||
{
|
||||
public string v { get; } = "a";
|
||||
|
||||
[Full]
|
||||
public string f { get; } = "b";
|
||||
}
|
||||
|
||||
private class QuoteValue : ParameterBase
|
||||
{
|
||||
public static string pathValue = @"C:\Programe Files\Damn thats space";
|
||||
|
||||
[Quote]
|
||||
public string path { get; set; } = pathValue;
|
||||
}
|
||||
|
||||
private class FlagAndOptional : ParameterBase
|
||||
{
|
||||
public bool a { get; set; } = true;
|
||||
|
||||
public bool b { get; set; } = false;
|
||||
|
||||
[Optional]
|
||||
public string c { get; set; } = string.Empty;
|
||||
|
||||
[Optional]
|
||||
public string? d { get; set; } = null;
|
||||
}
|
||||
|
||||
private class RequiredEmpty : ParameterBase
|
||||
{
|
||||
public string? udp { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private class RequiredNull : ParameterBase
|
||||
{
|
||||
public string? udp { get; set; } = null;
|
||||
}
|
||||
|
||||
private class Number : ParameterBase
|
||||
{
|
||||
public ushort a { get; set; } = 1;
|
||||
|
||||
public int b { get; set; } = 1;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test()
|
||||
{
|
||||
Assert.AreEqual(new VerbAndRealName().ToString(), "-v a -v b");
|
||||
Assert.AreEqual(new Full().ToString(), "--f a --f b");
|
||||
Assert.AreEqual(new FullWithVerb().ToString(), "--f a -v b");
|
||||
Assert.AreEqual(new VerbWithFull().ToString(), "-v a --f b");
|
||||
Assert.AreEqual(new QuoteValue().ToString(), $"--path \"{QuoteValue.pathValue}\"");
|
||||
Assert.AreEqual(new FlagAndOptional().ToString(), "--a");
|
||||
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredEmpty().ToString(); });
|
||||
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredNull().ToString(); });
|
||||
Assert.AreEqual(new Number().ToString(), "--a 1 --b 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,10 @@ using Netch.Utils;
|
||||
namespace UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class TestParseShareLink : TestBase
|
||||
public class ParseShareLink : TestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestServerFromSSR()
|
||||
public void ParseSSR()
|
||||
{
|
||||
const string normalCase =
|
||||
"ssr://MTI3LjAuMC4xOjEyMzQ6YXV0aF9hZXMxMjhfbWQ1OmFlcy0xMjgtY2ZiOnRsczEuMl90aWNrZXRfYXV0aDpZV0ZoWW1KaS8_b2Jmc3BhcmFtPVluSmxZV3QzWVRFeExtMXZaUQ";
|
||||
|
||||
26
UnitTest/SuffixVersionTest.cs
Normal file
26
UnitTest/SuffixVersionTest.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Netch.Models.GitHubRelease;
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class SuffixVersionTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void Test()
|
||||
{
|
||||
var rel = SuffixVersion.Parse("1.0.0");
|
||||
var a1 = SuffixVersion.Parse("1.0.0-Alpha1");
|
||||
var a3 = SuffixVersion.Parse("1.0.0-aLpHa3");
|
||||
var b2 = SuffixVersion.Parse("1.0.0-betA2");
|
||||
|
||||
Assert.AreEqual(rel.ToString(), "1.0.0");
|
||||
Assert.AreEqual(a1.ToString(), "1.0.0-Alpha1");
|
||||
Assert.IsTrue(rel.CompareTo(a1) > 0);
|
||||
Assert.IsTrue(rel.CompareTo(b2) > 0);
|
||||
|
||||
Assert.IsTrue(b2.CompareTo(a1) > 0);
|
||||
Assert.IsTrue(b2.CompareTo(a3) > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
using Netch;
|
||||
|
||||
namespace UnitTest
|
||||
namespace UnitTest
|
||||
{
|
||||
public class TestBase
|
||||
{
|
||||
protected TestBase()
|
||||
{
|
||||
#if DEBUG
|
||||
Global.Testing = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -23,8 +24,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
2
binaries
2
binaries
Submodule binaries updated: 61b435e28c...db78c29ec3
2
modes
2
modes
Submodule modes updated: 42375ef724...72ad96d018
Reference in New Issue
Block a user