- Update Project and Libraries to .Net 7

- Update Nuget Packages
- Update Namespaces to C# 10 Style
- Change BinaryFormatter to XmlSerializer
This commit is contained in:
Ali Jebali 2023-08-28 13:15:36 +03:30
parent 4f30e3f0e3
commit e1cd0bbf63
86 changed files with 14564 additions and 14665 deletions

View file

@ -1,20 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resources.Designer.cs"> <Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
<EmbeddedResource Update="Resources.resx"> <EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project>
</Project>

View file

@ -1,20 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ItemGroup> <Protobuf Include="Statistics.proto" />
<Protobuf Include="Statistics.proto" /> </ItemGroup>
</ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.24.2" />
<ItemGroup> <PackageReference Include="Grpc.Net.Client" Version="2.56.0" />
<PackageReference Include="Google.Protobuf" Version="3.23.3" /> <PackageReference Include="Grpc.Tools" Version="2.57.0">
<PackageReference Include="Grpc.Net.Client" Version="2.54.0" /> <PrivateAssets>all</PrivateAssets>
<PackageReference Include="Grpc.Tools" Version="2.54.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> </PackageReference>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </ItemGroup>
</PackageReference> </Project>
</ItemGroup>
</Project>

View file

@ -1,13 +1,12 @@
using ProtosLib.Statistics; using ProtosLib.Statistics;
namespace ProtosLib namespace ProtosLib;
{
public class Tests
{
private StatsService.StatsServiceClient client_;
public Tests() public class Tests
{ {
} private StatsService.StatsServiceClient client_;
public Tests()
{
} }
} }

View file

@ -4,87 +4,86 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Tool; using v2rayN.Tool;
namespace v2rayN namespace v2rayN;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{ {
/// <summary> public static EventWaitHandle ProgramStarted;
/// Interaction logic for App.xaml private static Config _config;
/// </summary>
public partial class App : Application public App()
{ {
public static EventWaitHandle ProgramStarted; // Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
private static Config _config; this.DispatcherUnhandledException += App_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
public App() /// <summary>
/// 只打开一个进程
/// </summary>
/// <param name="e"></param>
protected override void OnStartup(StartupEventArgs e)
{
Global.ExePathKey = Utils.GetMD5(Utils.GetExePath());
var rebootas = (e.Args ?? new string[] { }).Any(t => t == Global.RebootAs);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Global.ExePathKey, out bool bCreatedNew);
if (!rebootas && !bCreatedNew)
{ {
// Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); ProgramStarted.Set();
this.DispatcherUnhandledException += App_DispatcherUnhandledException; Current.Shutdown();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Environment.Exit(0);
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; return;
} }
/// <summary> Global.processJob = new Job();
/// 只打开一个进程
/// </summary> Logging.Setup();
/// <param name="e"></param> Init();
protected override void OnStartup(StartupEventArgs e) Logging.LoggingEnabled(_config.guiItem.enableLog);
Utils.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.ClearLogs();
Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage);
base.OnStartup(e);
}
private void Init()
{
if (ConfigHandler.LoadConfig(ref _config) != 0)
{ {
Global.ExePathKey = Utils.GetMD5(Utils.GetExePath()); UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用");
Application.Current.Shutdown();
var rebootas = (e.Args ?? new string[] { }).Any(t => t == Global.RebootAs); Environment.Exit(0);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Global.ExePathKey, out bool bCreatedNew); return;
if (!rebootas && !bCreatedNew)
{
ProgramStarted.Set();
Current.Shutdown();
Environment.Exit(0);
return;
}
Global.processJob = new Job();
Logging.Setup();
Init();
Logging.LoggingEnabled(_config.guiItem.enableLog);
Utils.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.ClearLogs();
Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage);
base.OnStartup(e);
} }
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
//{
// _config.guiItem.enableStatistics = false;
//}
}
private void Init() private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{ {
if (ConfigHandler.LoadConfig(ref _config) != 0) Utils.SaveLog("App_DispatcherUnhandledException", e.Exception);
{ e.Handled = true;
UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用"); }
Application.Current.Shutdown();
Environment.Exit(0);
return;
}
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
//{
// _config.guiItem.enableStatistics = false;
//}
}
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject != null)
{ {
Utils.SaveLog("App_DispatcherUnhandledException", e.Exception); Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
e.Handled = true;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject != null)
{
Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
}
}
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
} }
} }
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
}
} }

View file

@ -2,180 +2,179 @@
using System.IO; using System.IO;
using System.Net; using System.Net;
namespace v2rayN.Base namespace v2rayN.Base;
internal class DownloaderHelper
{ {
internal class DownloaderHelper private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
private static readonly Lazy<DownloaderHelper> _instance = new(() => new()); if (string.IsNullOrEmpty(url))
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
if (string.IsNullOrEmpty(url)) return null;
{
return null;
}
Uri uri = new(url);
//Authorization Header
var headers = new WebHeaderCollection();
if (!Utils.IsNullOrEmpty(uri.UserInfo))
{
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Headers = headers,
UserAgent = userAgent,
Timeout = timeout * 1000,
Proxy = webProxy
}
};
using var downloader = new DownloadService(downloadOpt);
downloader.DownloadFileCompleted += (sender, value) =>
{
if (value.Error != null)
{
throw value.Error;
}
};
using var cts = new CancellationTokenSource();
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
using StreamReader reader = new(stream);
downloadOpt = null;
return reader.ReadToEnd();
} }
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout) Uri uri = new(url);
//Authorization Header
var headers = new WebHeaderCollection();
if (!Utils.IsNullOrEmpty(uri.UserInfo))
{ {
if (string.IsNullOrEmpty(url)) headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
{
throw new ArgumentNullException(nameof(url));
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
var hasValue = false;
double maxSpeed = 0;
using var downloader = new DownloadService(downloadOpt);
//downloader.DownloadStarted += (sender, value) =>
//{
// if (progress != null)
// {
// progress.Report("Start download data...");
// }
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
TimeSpan ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
hasValue = true;
totalSecond = ts.Seconds;
if (value.BytesPerSecondSpeed > maxSpeed)
{
maxSpeed = value.BytesPerSecondSpeed;
var speed = (maxSpeed / 1000 / 1000).ToString("#0.0");
progress.Report(speed);
}
}
};
downloader.DownloadFileCompleted += (sender, value) =>
{
if (progress != null)
{
if (!hasValue && value.Error != null)
{
progress.Report(value.Error?.Message);
}
}
};
//progress.Report("......");
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeout * 1000);
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
downloadOpt = null;
} }
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout) var downloadOpt = new DownloadConfiguration()
{ {
if (string.IsNullOrEmpty(url)) Timeout = timeout * 1000,
{ MaxTryAgainOnFailover = 2,
throw new ArgumentNullException(nameof(url)); RequestConfiguration =
}
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}
var downloadOpt = new DownloadConfiguration()
{ {
Headers = headers,
UserAgent = userAgent,
Timeout = timeout * 1000, Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2, Proxy = webProxy
RequestConfiguration = }
{ };
Timeout= timeout * 1000,
Proxy = webProxy
}
};
var progressPercentage = 0; using var downloader = new DownloadService(downloadOpt);
var hasValue = false; downloader.DownloadFileCompleted += (sender, value) =>
using var downloader = new DownloadService(downloadOpt); {
downloader.DownloadStarted += (sender, value) => if (value.Error != null)
{ {
progress?.Report(0); throw value.Error;
}; }
downloader.DownloadProgressChanged += (sender, value) => };
using var cts = new CancellationTokenSource();
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
using StreamReader reader = new(stream);
downloadOpt = null;
return reader.ReadToEnd();
}
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
var hasValue = false;
double maxSpeed = 0;
using var downloader = new DownloadService(downloadOpt);
//downloader.DownloadStarted += (sender, value) =>
//{
// if (progress != null)
// {
// progress.Report("Start download data...");
// }
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
TimeSpan ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{ {
hasValue = true; hasValue = true;
var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100); totalSecond = ts.Seconds;
if (progressPercentage != percent && percent % 10 == 0) if (value.BytesPerSecondSpeed > maxSpeed)
{ {
progressPercentage = percent; maxSpeed = value.BytesPerSecondSpeed;
progress.Report(percent); var speed = (maxSpeed / 1000 / 1000).ToString("#0.0");
progress.Report(speed);
} }
}; }
downloader.DownloadFileCompleted += (sender, value) => };
downloader.DownloadFileCompleted += (sender, value) =>
{
if (progress != null)
{ {
if (progress != null) if (!hasValue && value.Error != null)
{ {
if (hasValue && value.Error == null) progress.Report(value.Error?.Message);
{
progress.Report(101);
}
} }
}; }
};
//progress.Report("......");
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeout * 1000);
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
using var cts = new CancellationTokenSource(); downloadOpt = null;
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token); }
downloadOpt = null; public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
} }
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
var progressPercentage = 0;
var hasValue = false;
using var downloader = new DownloadService(downloadOpt);
downloader.DownloadStarted += (sender, value) =>
{
progress?.Report(0);
};
downloader.DownloadProgressChanged += (sender, value) =>
{
hasValue = true;
var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100);
if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress.Report(percent);
}
};
downloader.DownloadFileCompleted += (sender, value) =>
{
if (progress != null)
{
if (hasValue && value.Error == null)
{
progress.Report(101);
}
}
};
using var cts = new CancellationTokenSource();
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
downloadOpt = null;
} }
} }

View file

@ -3,154 +3,153 @@ using System.Net.Http;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
namespace v2rayN.Base namespace v2rayN.Base;
/// <summary>
/// </summary>
public class HttpClientHelper
{ {
/// <summary> private static readonly Lazy<HttpClientHelper> _instance = new(() =>
/// </summary>
public class HttpClientHelper
{ {
private static readonly Lazy<HttpClientHelper> _instance = new(() => HttpClientHandler handler = new() { UseCookies = false };
HttpClientHelper helper = new(new HttpClient(handler));
return helper;
});
public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
public async Task<string?> GetAsync(string url)
{
if (string.IsNullOrEmpty(url)) return null;
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (string.IsNullOrWhiteSpace(url)) return null;
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
var jsonContent = Utils.ToJson(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content);
}
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName)) File.Delete(fileName);
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString());
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
using var file = File.Create(fileName);
var totalRead = 0L;
var buffer = new byte[1024 * 1024];
var progressPercentage = 0;
while (true)
{ {
HttpClientHandler handler = new() { UseCookies = false }; token.ThrowIfCancellationRequested();
HttpClientHelper helper = new(new HttpClient(handler));
return helper;
});
public static HttpClientHelper Instance => _instance.Value; var read = await stream.ReadAsync(buffer, token);
private readonly HttpClient httpClient; totalRead += read;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient; if (read == 0) break;
file.Write(buffer, 0, read);
public async Task<string?> GetAsync(string url)
{
if (string.IsNullOrEmpty(url)) return null;
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (string.IsNullOrWhiteSpace(url)) return null;
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
var jsonContent = Utils.ToJson(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content);
}
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName)) File.Delete(fileName);
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString());
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
using var file = File.Create(fileName);
var totalRead = 0L;
var buffer = new byte[1024 * 1024];
var progressPercentage = 0;
while (true)
{
token.ThrowIfCancellationRequested();
var read = await stream.ReadAsync(buffer, token);
totalRead += read;
if (read == 0) break;
file.Write(buffer, 0, read);
if (canReportProgress)
{
var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress!.Report(percent);
}
}
}
if (canReportProgress) if (canReportProgress)
{ {
progress!.Report(101); var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress!.Report(percent);
}
} }
} }
if (canReportProgress)
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{ {
if (string.IsNullOrEmpty(url)) progress!.Report(101);
}
}
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
}
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
//var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
string progressSpeed = string.Empty;
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
do
{
if (token.IsCancellationRequested)
{ {
throw new ArgumentNullException(nameof(url)); if (totalRead > 0)
}
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
//var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
string progressSpeed = string.Empty;
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
do
{
if (token.IsCancellationRequested)
{ {
if (totalRead > 0) return;
{
return;
}
else
{
token.ThrowIfCancellationRequested();
}
}
var read = await stream.ReadAsync(buffer, token);
if (read == 0)
{
isMoreToRead = false;
} }
else else
{ {
var data = new byte[read]; token.ThrowIfCancellationRequested();
buffer.ToList().CopyTo(0, data, 0, read); }
}
totalRead += read; var read = await stream.ReadAsync(buffer, token);
TimeSpan ts = (DateTime.Now - totalDatetime); if (read == 0)
if (progress != null && ts.Seconds > totalSecond) {
isMoreToRead = false;
}
else
{
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);
totalRead += read;
TimeSpan ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0");
if (progressSpeed != speed)
{ {
totalSecond = ts.Seconds; progressSpeed = speed;
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0"); progress.Report(speed);
if (progressSpeed != speed)
{
progressSpeed = speed;
progress.Report(speed);
}
} }
} }
} while (isMoreToRead); }
} } while (isMoreToRead);
} }
} }

View file

@ -1,9 +1,8 @@
using System.Windows.Controls; using System.Windows.Controls;
namespace v2rayN.Base namespace v2rayN.Base;
internal class MyDGTextColumn : DataGridTextColumn
{ {
internal class MyDGTextColumn : DataGridTextColumn public string ExName { get; set; }
{
public string ExName { get; set; }
}
} }

View file

@ -1,122 +1,121 @@
using SQLite; using SQLite;
using System.Collections; using System.Collections;
namespace v2rayN.Base namespace v2rayN.Base;
public sealed class SqliteHelper
{ {
public sealed class SqliteHelper private static readonly Lazy<SqliteHelper> _instance = new(() => new());
public static SqliteHelper Instance => _instance.Value;
private string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private static readonly object objLock = new();
public SqliteHelper()
{ {
private static readonly Lazy<SqliteHelper> _instance = new(() => new()); _connstr = Utils.GetConfigPath(Global.ConfigDB);
public static SqliteHelper Instance => _instance.Value; _db = new SQLiteConnection(_connstr, false);
private string _connstr; _dbAsync = new SQLiteAsyncConnection(_connstr, false);
private SQLiteConnection _db; }
private SQLiteAsyncConnection _dbAsync;
private static readonly object objLock = new();
public SqliteHelper() public CreateTableResult CreateTable<T>()
{ {
_connstr = Utils.GetConfigPath(Global.ConfigDB); return _db.CreateTable<T>();
_db = new SQLiteConnection(_connstr, false); }
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
}
public CreateTableResult CreateTable<T>() public int Insert(object model)
{ {
return _db.CreateTable<T>(); return _db.Insert(model);
} }
public int Insert(object model) public int InsertAll(IEnumerable models)
{
lock (objLock)
{ {
return _db.Insert(model); return _db.InsertAll(models);
}
public int InsertAll(IEnumerable models)
{
lock (objLock)
{
return _db.InsertAll(models);
}
}
public async Task<int> InsertAsync(object model)
{
return await _dbAsync.InsertAsync(model);
}
public int Replace(object model)
{
lock (objLock)
{
return _db.InsertOrReplace(model);
}
}
public async Task<int> Replacesync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public int Update(object model)
{
lock (objLock)
{
return _db.Update(model);
}
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public int UpdateAll(IEnumerable models)
{
lock (objLock)
{
return _db.UpdateAll(models);
}
}
public int Delete(object model)
{
lock (objLock)
{
return _db.Delete(model);
}
}
public async Task<int> DeleteAsync(object model)
{
return await _dbAsync.DeleteAsync(model);
}
public List<T> Query<T>(string sql) where T : new()
{
return _db.Query<T>(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public int Execute(string sql)
{
return _db.Execute(sql);
}
public async Task<int> ExecuteAsync(string sql)
{
return await _dbAsync.ExecuteAsync(sql);
}
public TableQuery<T> Table<T>() where T : new()
{
return _db.Table<T>();
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
{
return _dbAsync.Table<T>();
} }
} }
public async Task<int> InsertAsync(object model)
{
return await _dbAsync.InsertAsync(model);
}
public int Replace(object model)
{
lock (objLock)
{
return _db.InsertOrReplace(model);
}
}
public async Task<int> Replacesync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public int Update(object model)
{
lock (objLock)
{
return _db.Update(model);
}
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public int UpdateAll(IEnumerable models)
{
lock (objLock)
{
return _db.UpdateAll(models);
}
}
public int Delete(object model)
{
lock (objLock)
{
return _db.Delete(model);
}
}
public async Task<int> DeleteAsync(object model)
{
return await _dbAsync.DeleteAsync(model);
}
public List<T> Query<T>(string sql) where T : new()
{
return _db.Query<T>(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public int Execute(string sql)
{
return _db.Execute(sql);
}
public async Task<int> ExecuteAsync(string sql)
{
return await _dbAsync.ExecuteAsync(sql);
}
public TableQuery<T> Table<T>() where T : new()
{
return _db.Table<T>();
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
{
return _dbAsync.Table<T>();
}
} }

View file

@ -1,84 +1,83 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
namespace v2rayN.Base namespace v2rayN.Base;
internal static class StringEx
{ {
internal static class StringEx public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{ {
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) return string.IsNullOrEmpty(value);
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
{
return string.IsNullOrWhiteSpace(value);
}
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty()) return false;
return chars.Contains(s[0]);
}
public static bool IsWhiteSpace(this string value)
{
foreach (char c in value)
{ {
return string.IsNullOrEmpty(value); if (char.IsWhiteSpace(c)) continue;
return false;
} }
return true;
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
string? line;
while ((line = reader.ReadLine()) != null)
{ {
return string.IsNullOrWhiteSpace(value); if (line.IsWhiteSpace()) continue;
} yield return line;
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty()) return false;
return chars.Contains(s[0]);
}
public static bool IsWhiteSpace(this string value)
{
foreach (char c in value)
{
if (char.IsWhiteSpace(c)) continue;
return false;
}
return true;
}
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
string? line;
while ((line = reader.ReadLine()) != null)
{
if (line.IsWhiteSpace()) continue;
yield return line;
}
}
public static string TrimEx(this string? value)
{
return value == null ? string.Empty : value.Trim();
}
public static string RemovePrefix(this string value, char prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(1);
}
else
{
return value;
}
}
public static string RemovePrefix(this string value, string prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(prefix.Length);
}
else
{
return value;
}
}
public static string UpperFirstChar(this string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return char.ToUpper(value[0]) + value.Substring(1);
} }
} }
public static string TrimEx(this string? value)
{
return value == null ? string.Empty : value.Trim();
}
public static string RemovePrefix(this string value, char prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(1);
}
else
{
return value;
}
}
public static string RemovePrefix(this string value, string prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(prefix.Length);
}
else
{
return value;
}
}
public static string UpperFirstChar(this string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return char.ToUpper(value[0]) + value.Substring(1);
}
} }

View file

@ -1,25 +1,24 @@
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Media; using System.Windows.Media;
namespace v2rayN.Converters namespace v2rayN.Converters;
public class DelayColorConverter : IValueConverter
{ {
public class DelayColorConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) int.TryParse(value.ToString(), out var delay);
{
int.TryParse(value.ToString(), out var delay);
if (delay <= 0) if (delay <= 0)
return new SolidColorBrush(Colors.Red); return new SolidColorBrush(Colors.Red);
if (delay <= 200) if (delay <= 200)
return new SolidColorBrush(Colors.Green); return new SolidColorBrush(Colors.Green);
else else
return new SolidColorBrush(Colors.IndianRed); return new SolidColorBrush(Colors.IndianRed);
} }
public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ {
return null; return null;
}
} }
} }

View file

@ -1,24 +1,23 @@
using System.Globalization; using System.Globalization;
using System.Windows.Data; using System.Windows.Data;
namespace v2rayN.Converters namespace v2rayN.Converters;
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{ {
[ValueConversion(typeof(bool), typeof(bool))] public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public class InverseBooleanConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) if (targetType != typeof(bool))
{ {
if (targetType != typeof(bool)) throw new InvalidOperationException("The target must be a boolean");
{
throw new InvalidOperationException("The target must be a boolean");
}
return !(bool)value;
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) return !(bool)value;
{ }
throw new NotImplementedException();
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
} }
} }

View file

@ -1,27 +1,26 @@
using System.Windows.Media; using System.Windows.Media;
using v2rayN.Handler; using v2rayN.Handler;
namespace v2rayN.Converters namespace v2rayN.Converters;
{
public class MaterialDesignFonts
{
public static FontFamily MyFont { get; }
static MaterialDesignFonts() public class MaterialDesignFonts
{
public static FontFamily MyFont { get; }
static MaterialDesignFonts()
{
try
{ {
try var fontFamily = LazyConfig.Instance.GetConfig().uiItem.currentFontFamily;
if (!string.IsNullOrEmpty(fontFamily))
{ {
var fontFamily = LazyConfig.Instance.GetConfig().uiItem.currentFontFamily; var fontPath = Utils.GetFontsPath();
if (!string.IsNullOrEmpty(fontFamily)) MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}");
{
var fontPath = Utils.GetFontsPath();
MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}");
}
} }
catch
{
}
MyFont ??= new FontFamily("Microsoft YaHei");
} }
catch
{
}
MyFont ??= new FontFamily("Microsoft YaHei");
} }
} }

View file

@ -1,174 +1,173 @@
namespace v2rayN namespace v2rayN;
internal class Global
{ {
internal class Global #region const
public const string githubUrl = "https://github.com";
public const string githubApiUrl = "https://api.github.com/repos";
public const string v2rayWebsiteUrl = @"https://www.v2fly.org/";
public const string AboutUrl = @"https://github.com/2dust/v2rayN";
public const string UpdateUrl = AboutUrl + @"/releases";
public const string v2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
public const string xrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases";
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases";
public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases";
public const string hysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
public const string naiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases";
public const string singboxCoreUrl = "https://github.com/SagerNet/sing-box/releases";
public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string singboxGeoUrl = "https://github.com/soffchen/sing-{0}/releases/latest/download/{0}.db";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string juicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json";
public const string ConfigDB = "guiNDB.db";
public const string coreConfigFileName = "config.json";
public const string corePreConfigFileName = "configPre.json";
public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig";
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
public const string v2raySampleHttprequestFileName = "v2rayN.Sample.SampleHttprequest";
public const string v2raySampleHttpresponseFileName = "v2rayN.Sample.SampleHttpresponse";
public const string v2raySampleInbound = "v2rayN.Sample.SampleInbound";
public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_";
public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns";
public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound";
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal";
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp";
public const string TcpHeaderHttp = "http";
public const string None = "none";
public const string agentTag = "proxy";
public const string directTag = "direct";
public const string blockTag = "block";
public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality";
public const string InboundSocks = "socks";
public const string InboundHttp = "http";
public const string InboundSocks2 = "socks2";
public const string InboundHttp2 = "http2";
public const string Loopback = "127.0.0.1";
public const string InboundAPITagName = "api";
public const string InboundAPIProtocal = "dokodemo-door";
public const string vmessProtocol = "vmess://";
public const string vmessProtocolLite = "vmess";
public const string ssProtocol = "ss://";
public const string ssProtocolLite = "shadowsocks";
public const string socksProtocol = "socks://";
public const string socksProtocolLite = "socks";
public const string httpProtocol = "http://";
public const string httpsProtocol = "https://";
public const string vlessProtocol = "vless://";
public const string vlessProtocolLite = "vless";
public const string trojanProtocol = "trojan://";
public const string trojanProtocolLite = "trojan";
public const string userEMail = "t@t.tt";
public const string MyRegPath = "Software\\v2rayNGUI";
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun";
public const string MyRegKeyLanguage = "CurrentLanguage";
public const string CustomIconName = "v2rayN.ico";
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string RoutingRuleComma = "<COMMA>";
public const string GrpcgunMode = "gun";
public const string GrpcmultiMode = "multi";
public const int MaxPort = 65536;
public const string CommandClearMsg = "CommandClearMsg";
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 10;
public const string RebootAs = "rebootas";
public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}",
"socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
""
};
public static readonly List<string> SubConvertUrls = new List<string> {
@"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}",
""
};
public static readonly List<string> SubConvertConfig = new List<string> {
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
};
public static readonly List<string> SubConvertTargets = new List<string> {
"",
"mixed",
"v2ray",
"clash",
"ss",
};
public static readonly List<string> SpeedTestUrls = new() {
@"http://cachefly.cachefly.net/100mb.test",
@"http://cachefly.cachefly.net/10mb.test"
};
public static readonly Dictionary<string, string> userAgentTxt = new()
{ {
#region const {"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
{"none",""}
};
public const string githubUrl = "https://github.com"; public static readonly List<string> vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
public const string githubApiUrl = "https://api.github.com/repos"; public static readonly List<string> ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
public const string v2rayWebsiteUrl = @"https://www.v2fly.org/"; public static readonly List<string> ssSecuritysInSagerNet = new() { "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "rc4", "rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "bf-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "camellia-128-cfb8", "camellia-192-cfb8", "camellia-256-cfb8", "salsa20", "chacha20", "chacha20-ietf", "xchacha20" };
public const string AboutUrl = @"https://github.com/2dust/v2rayN"; public static readonly List<string> ssSecuritysInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public const string UpdateUrl = AboutUrl + @"/releases"; public static readonly List<string> flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public const string v2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases"; public static readonly List<string> networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" };
public const string xrayCoreUrl = "https://github.com/XTLS/Xray-core/releases"; public static readonly List<string> kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases"; public static readonly List<string> coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" };
public const string NUrl = @"https://github.com/2dust/v2rayN/releases"; public static readonly List<string> coreTypes4VLESS = new() { "Xray", "sing_box" };
public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases"; public static readonly List<string> domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases"; public static readonly List<string> domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public const string hysteriaCoreUrl = "https://github.com/apernet/hysteria/releases"; public static readonly List<string> domainMatchers = new() { "linear", "mph", "" };
public const string naiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases"; public static readonly List<string> fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases"; public static readonly List<string> userAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public const string singboxCoreUrl = "https://github.com/SagerNet/sing-box/releases";
public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string singboxGeoUrl = "https://github.com/soffchen/sing-{0}/releases/latest/download/{0}.db";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string juicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public static readonly List<string> allowInsecures = new() { "true", "false", "" };
public const string ConfigFileName = "guiNConfig.json"; public static readonly List<string> domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public const string ConfigDB = "guiNDB.db"; public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
public const string coreConfigFileName = "config.json"; public static readonly List<string> alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" };
public const string corePreConfigFileName = "configPre.json"; public static readonly List<string> LogLevel = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
public static readonly List<string> Protocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> TunMtus = new() { "9000", "1500" };
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig"; #endregion const
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
public const string v2raySampleHttprequestFileName = "v2rayN.Sample.SampleHttprequest";
public const string v2raySampleHttpresponseFileName = "v2rayN.Sample.SampleHttpresponse";
public const string v2raySampleInbound = "v2rayN.Sample.SampleInbound";
public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_";
public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns"; #region global variable
public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound";
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal"; public static int statePort { get; set; }
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal"; public static Job processJob { get; set; }
public static bool ShowInTaskbar { get; set; }
public static string ExePathKey { get; set; }
public const string DefaultSecurity = "auto"; #endregion global variable
public const string DefaultNetwork = "tcp";
public const string TcpHeaderHttp = "http";
public const string None = "none";
public const string agentTag = "proxy";
public const string directTag = "direct";
public const string blockTag = "block";
public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality";
public const string InboundSocks = "socks";
public const string InboundHttp = "http";
public const string InboundSocks2 = "socks2";
public const string InboundHttp2 = "http2";
public const string Loopback = "127.0.0.1";
public const string InboundAPITagName = "api";
public const string InboundAPIProtocal = "dokodemo-door";
public const string vmessProtocol = "vmess://";
public const string vmessProtocolLite = "vmess";
public const string ssProtocol = "ss://";
public const string ssProtocolLite = "shadowsocks";
public const string socksProtocol = "socks://";
public const string socksProtocolLite = "socks";
public const string httpProtocol = "http://";
public const string httpsProtocol = "https://";
public const string vlessProtocol = "vless://";
public const string vlessProtocolLite = "vless";
public const string trojanProtocol = "trojan://";
public const string trojanProtocolLite = "trojan";
public const string userEMail = "t@t.tt";
public const string MyRegPath = "Software\\v2rayNGUI";
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun";
public const string MyRegKeyLanguage = "CurrentLanguage";
public const string CustomIconName = "v2rayN.ico";
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string RoutingRuleComma = "<COMMA>";
public const string GrpcgunMode = "gun";
public const string GrpcmultiMode = "multi";
public const int MaxPort = 65536;
public const string CommandClearMsg = "CommandClearMsg";
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 10;
public const string RebootAs = "rebootas";
public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}",
"socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
""
};
public static readonly List<string> SubConvertUrls = new List<string> {
@"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}",
""
};
public static readonly List<string> SubConvertConfig = new List<string> {
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
};
public static readonly List<string> SubConvertTargets = new List<string> {
"",
"mixed",
"v2ray",
"clash",
"ss",
};
public static readonly List<string> SpeedTestUrls = new() {
@"http://cachefly.cachefly.net/100mb.test",
@"http://cachefly.cachefly.net/10mb.test"
};
public static readonly Dictionary<string, string> userAgentTxt = new()
{
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
{"none",""}
};
public static readonly List<string> vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
public static readonly List<string> ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
public static readonly List<string> ssSecuritysInSagerNet = new() { "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "rc4", "rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "bf-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "camellia-128-cfb8", "camellia-192-cfb8", "camellia-256-cfb8", "salsa20", "chacha20", "chacha20-ietf", "xchacha20" };
public static readonly List<string> ssSecuritysInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" };
public static readonly List<string> kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" };
public static readonly List<string> coreTypes4VLESS = new() { "Xray", "sing_box" };
public static readonly List<string> domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public static readonly List<string> domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public static readonly List<string> domainMatchers = new() { "linear", "mph", "" };
public static readonly List<string> fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public static readonly List<string> userAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public static readonly List<string> allowInsecures = new() { "true", "false", "" };
public static readonly List<string> domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
public static readonly List<string> alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" };
public static readonly List<string> LogLevel = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
public static readonly List<string> Protocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> TunMtus = new() { "9000", "1500" };
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
#endregion const
#region global variable
public static int statePort { get; set; }
public static Job processJob { get; set; }
public static bool ShowInTaskbar { get; set; }
public static string ExePathKey { get; set; }
#endregion global variable
}
} }

File diff suppressed because it is too large Load diff

View file

@ -2,155 +2,154 @@
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
/// <summary>
/// Core configuration file processing class
/// </summary>
internal class CoreConfigHandler
{ {
/// <summary> public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content)
/// Core configuration file processing class
/// </summary>
internal class CoreConfigHandler
{ {
public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content) content = string.Empty;
try
{ {
content = string.Empty; if (node == null)
try
{ {
if (node == null) msg = ResUI.CheckServerSettings;
return -1;
}
var config = LazyConfig.Instance.GetConfig();
msg = ResUI.InitialConfiguration;
if (node.configType == EConfigType.Custom)
{
return GenerateClientCustomConfig(node, fileName, out msg);
}
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
{
var configGenSingbox = new CoreConfigSingbox(config);
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
{ {
msg = ResUI.CheckServerSettings;
return -1; return -1;
} }
var config = LazyConfig.Instance.GetConfig(); if (Utils.IsNullOrEmpty(fileName))
msg = ResUI.InitialConfiguration;
if (node.configType == EConfigType.Custom)
{ {
return GenerateClientCustomConfig(node, fileName, out msg); content = Utils.ToJson(singboxConfig);
}
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
{
var configGenSingbox = new CoreConfigSingbox(config);
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
{
return -1;
}
if (Utils.IsNullOrEmpty(fileName))
{
content = Utils.ToJson(singboxConfig);
}
else
{
Utils.ToJsonFile(singboxConfig, fileName, false);
}
} }
else else
{ {
var coreConfigV2ray = new CoreConfigV2ray(config); Utils.ToJsonFile(singboxConfig, fileName, false);
if (coreConfigV2ray.GenerateClientConfigContent(node, out V2rayConfig? v2rayConfig, out msg) != 0)
{
return -1;
}
if (Utils.IsNullOrEmpty(fileName))
{
content = Utils.ToJson(v2rayConfig);
}
else
{
Utils.ToJsonFile(v2rayConfig, fileName, false);
}
} }
} }
catch (Exception ex) else
{
var coreConfigV2ray = new CoreConfigV2ray(config);
if (coreConfigV2ray.GenerateClientConfigContent(node, out V2rayConfig? v2rayConfig, out msg) != 0)
{
return -1;
}
if (Utils.IsNullOrEmpty(fileName))
{
content = Utils.ToJson(v2rayConfig);
}
else
{
Utils.ToJsonFile(v2rayConfig, fileName, false);
}
}
}
catch (Exception ex)
{
Utils.SaveLog("GenerateClientConfig", ex);
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
return 0;
}
private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg)
{
try
{
if (node == null || fileName is null)
{
msg = ResUI.CheckServerSettings;
return -1;
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}
string addressFileName = node.address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
}
if (!File.Exists(addressFileName))
{ {
Utils.SaveLog("GenerateClientConfig", ex);
msg = ResUI.FailedGenDefaultConfiguration; msg = ResUI.FailedGenDefaultConfiguration;
return -1; return -1;
} }
return 0; File.Copy(addressFileName, fileName);
}
private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg) //check again
{ if (!File.Exists(fileName))
try
{ {
if (node == null || fileName is null)
{
msg = ResUI.CheckServerSettings;
return -1;
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}
string addressFileName = node.address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
}
if (!File.Exists(addressFileName))
{
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
File.Copy(addressFileName, fileName);
//check again
if (!File.Exists(fileName))
{
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
//overwrite port
if (node.preSocksPort <= 0)
{
var fileContent = File.ReadAllLines(fileName).ToList();
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
switch (coreType)
{
case ECoreType.v2fly:
case ECoreType.SagerNet:
case ECoreType.Xray:
case ECoreType.v2fly_v5:
break;
case ECoreType.clash:
case ECoreType.clash_meta:
//remove the original
var indexPort = fileContent.FindIndex(t => t.Contains("port:"));
if (indexPort >= 0)
{
fileContent.RemoveAt(indexPort);
}
indexPort = fileContent.FindIndex(t => t.Contains("socks-port:"));
if (indexPort >= 0)
{
fileContent.RemoveAt(indexPort);
}
fileContent.Add($"port: {LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}");
fileContent.Add($"socks-port: {LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}");
break;
}
File.WriteAllLines(fileName, fileContent);
}
msg = string.Format(ResUI.SuccessfulConfiguration, "");
}
catch (Exception ex)
{
Utils.SaveLog("GenerateClientCustomConfig", ex);
msg = ResUI.FailedGenDefaultConfiguration; msg = ResUI.FailedGenDefaultConfiguration;
return -1; return -1;
} }
return 0;
}
public static string GenerateClientSpeedtestConfigString(Config config, List<ServerTestItem> selecteds, out string msg) //overwrite port
{ if (node.preSocksPort <= 0)
var coreConfigV2ray = new CoreConfigV2ray(config); {
return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg); var fileContent = File.ReadAllLines(fileName).ToList();
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
switch (coreType)
{
case ECoreType.v2fly:
case ECoreType.SagerNet:
case ECoreType.Xray:
case ECoreType.v2fly_v5:
break;
case ECoreType.clash:
case ECoreType.clash_meta:
//remove the original
var indexPort = fileContent.FindIndex(t => t.Contains("port:"));
if (indexPort >= 0)
{
fileContent.RemoveAt(indexPort);
}
indexPort = fileContent.FindIndex(t => t.Contains("socks-port:"));
if (indexPort >= 0)
{
fileContent.RemoveAt(indexPort);
}
fileContent.Add($"port: {LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}");
fileContent.Add($"socks-port: {LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}");
break;
}
File.WriteAllLines(fileName, fileContent);
}
msg = string.Format(ResUI.SuccessfulConfiguration, "");
} }
catch (Exception ex)
{
Utils.SaveLog("GenerateClientCustomConfig", ex);
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
return 0;
}
public static string GenerateClientSpeedtestConfigString(Config config, List<ServerTestItem> selecteds, out string msg)
{
var coreConfigV2ray = new CoreConfigV2ray(config);
return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg);
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,359 +4,358 @@ using System.Text;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
/// <summary>
/// Core process processing class
/// </summary>
internal class CoreHandler
{ {
/// <summary> private Config _config;
/// Core process processing class private Process? _process;
/// </summary> private Process? _processPre;
internal class CoreHandler private Action<bool, string> _updateFunc;
public CoreHandler(Config config, Action<bool, string> update)
{ {
private Config _config; _config = config;
private Process? _process; _updateFunc = update;
private Process? _processPre;
private Action<bool, string> _updateFunc;
public CoreHandler(Config config, Action<bool, string> update) Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
}
public void LoadCore()
{
var node = ConfigHandler.GetDefaultServer(ref _config);
if (node == null)
{ {
_config = config; ShowMsg(false, ResUI.CheckServerSettings);
_updateFunc = update; return;
Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
} }
public void LoadCore() string fileName = Utils.GetConfigPath(Global.coreConfigFileName);
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
{ {
var node = ConfigHandler.GetDefaultServer(ref _config); ShowMsg(false, msg);
if (node == null)
{
ShowMsg(false, ResUI.CheckServerSettings);
return;
}
string fileName = Utils.GetConfigPath(Global.coreConfigFileName);
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
{
ShowMsg(false, msg);
}
else
{
ShowMsg(false, msg);
ShowMsg(true, $"{node.GetSummary()}");
CoreStop();
CoreStart(node);
}
} }
else
public int LoadCoreConfigString(List<ServerTestItem> _selecteds)
{ {
int pid = -1; ShowMsg(false, msg);
string configStr = CoreConfigHandler.GenerateClientSpeedtestConfigString(_config, _selecteds, out string msg); ShowMsg(true, $"{node.GetSummary()}");
if (configStr == "") CoreStop();
{ CoreStart(node);
ShowMsg(false, msg);
}
else
{
ShowMsg(false, msg);
pid = CoreStartViaString(configStr);
}
return pid;
} }
}
public void CoreStop() public int LoadCoreConfigString(List<ServerTestItem> _selecteds)
{
int pid = -1;
string configStr = CoreConfigHandler.GenerateClientSpeedtestConfigString(_config, _selecteds, out string msg);
if (configStr == "")
{ {
try ShowMsg(false, msg);
}
else
{
ShowMsg(false, msg);
pid = CoreStartViaString(configStr);
}
return pid;
}
public void CoreStop()
{
try
{
bool hasProc = false;
if (_process != null)
{ {
bool hasProc = false; KillProcess(_process);
if (_process != null) _process.Dispose();
{ _process = null;
KillProcess(_process); hasProc = true;
_process.Dispose(); }
_process = null;
hasProc = true;
}
if (_processPre != null) if (_processPre != null)
{ {
KillProcess(_processPre); KillProcess(_processPre);
_processPre.Dispose(); _processPre.Dispose();
_processPre = null; _processPre = null;
hasProc = true; hasProc = true;
} }
if (!hasProc) if (!hasProc)
{
var coreInfos = LazyConfig.Instance.GetCoreInfos();
foreach (var it in coreInfos)
{ {
var coreInfos = LazyConfig.Instance.GetCoreInfos(); if (it.coreType == ECoreType.v2rayN)
foreach (var it in coreInfos)
{ {
if (it.coreType == ECoreType.v2rayN) continue;
}
foreach (string vName in it.coreExes)
{
Process[] existing = Process.GetProcessesByName(vName);
foreach (Process p in existing)
{ {
continue; string? path = p.MainModule?.FileName;
} if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe")
foreach (string vName in it.coreExes)
{
Process[] existing = Process.GetProcessesByName(vName);
foreach (Process p in existing)
{ {
string? path = p.MainModule?.FileName; KillProcess(p);
if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe")
{
KillProcess(p);
}
} }
} }
} }
} }
} }
catch (Exception ex) }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
public void CoreStopPid(int pid)
{
try
{
Process _p = Process.GetProcessById(pid);
KillProcess(_p);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private string CoreFindexe(CoreInfo coreInfo)
{
string fileName = string.Empty;
foreach (string name in coreInfo.coreExes)
{
string vName = $"{name}.exe";
vName = Utils.GetBinPath(vName, coreInfo.coreType);
if (File.Exists(vName))
{ {
Utils.SaveLog(ex.Message, ex); fileName = vName;
break;
} }
} }
if (Utils.IsNullOrEmpty(fileName))
public void CoreStopPid(int pid)
{ {
try string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl);
{ Utils.SaveLog(msg);
Process _p = Process.GetProcessById(pid); ShowMsg(false, msg);
KillProcess(_p);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
return fileName;
}
private string CoreFindexe(CoreInfo coreInfo) private void CoreStart(ProfileItem node)
{
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ECoreType coreType;
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
{ {
string fileName = string.Empty; coreType = ECoreType.sing_box;
foreach (string name in coreInfo.coreExes) }
else
{
coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
}
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
var displayLog = node.configType != EConfigType.Custom || node.displayLog;
var proc = RunProcess(node, coreInfo, "", displayLog, ShowMsg);
if (proc is null)
{
return;
}
_process = proc;
//start a socks service
if (_process != null && !_process.HasExited)
{
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
{ {
string vName = $"{name}.exe"; var itemSocks = new ProfileItem()
vName = Utils.GetBinPath(vName, coreInfo.coreType);
if (File.Exists(vName))
{ {
fileName = vName; coreType = ECoreType.sing_box,
break; configType = EConfigType.Socks,
address = Global.Loopback,
port = node.preSocksPort
};
string fileName2 = Utils.GetConfigPath(Global.corePreConfigFileName);
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
{
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box);
var proc2 = RunProcess(node, coreInfo2, $" -c {Global.corePreConfigFileName}", true, ShowMsg);
if (proc2 is not null)
{
_processPre = proc2;
}
} }
} }
}
}
private int CoreStartViaString(string configStr)
{
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
try
{
var coreInfo = LazyConfig.Instance.GetCoreInfo(ECoreType.Xray);
string fileName = CoreFindexe(coreInfo);
if (fileName == "") return -1;
Process p = new()
{
StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = "-config stdin:",
WorkingDirectory = Utils.GetConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
}
};
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
string msg = e.Data + Environment.NewLine;
ShowMsg(false, msg);
}
};
p.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
string msg = e.Data + Environment.NewLine;
ShowMsg(false, msg);
}
};
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.StandardInput.Write(configStr);
p.StandardInput.Close();
if (p.WaitForExit(1000))
{
throw new Exception(p.StandardError.ReadToEnd());
}
Global.processJob.AddProcess(p.Handle);
return p.Id;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
string msg = ex.Message;
ShowMsg(false, msg);
return -1;
}
}
private void ShowMsg(bool updateToTrayTooltip, string msg)
{
_updateFunc(updateToTrayTooltip, msg);
}
#region Process
private Process? RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog, Action<bool, string> update)
{
try
{
string fileName = CoreFindexe(coreInfo);
if (Utils.IsNullOrEmpty(fileName)) if (Utils.IsNullOrEmpty(fileName))
{ {
string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl); return null;
Utils.SaveLog(msg);
ShowMsg(false, msg);
} }
return fileName; Process proc = new()
}
private void CoreStart(ProfileItem node)
{
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ECoreType coreType;
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
{ {
coreType = ECoreType.sing_box; StartInfo = new ProcessStartInfo
}
else
{
coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
}
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
var displayLog = node.configType != EConfigType.Custom || node.displayLog;
var proc = RunProcess(node, coreInfo, "", displayLog, ShowMsg);
if (proc is null)
{
return;
}
_process = proc;
//start a socks service
if (_process != null && !_process.HasExited)
{
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
{ {
var itemSocks = new ProfileItem() FileName = fileName,
{ Arguments = string.Format(coreInfo.arguments, configPath),
coreType = ECoreType.sing_box, WorkingDirectory = Utils.GetConfigPath(),
configType = EConfigType.Socks, UseShellExecute = false,
address = Global.Loopback, RedirectStandardOutput = displayLog,
port = node.preSocksPort RedirectStandardError = displayLog,
}; CreateNoWindow = true,
string fileName2 = Utils.GetConfigPath(Global.corePreConfigFileName); StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0) StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
{
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box);
var proc2 = RunProcess(node, coreInfo2, $" -c {Global.corePreConfigFileName}", true, ShowMsg);
if (proc2 is not null)
{
_processPre = proc2;
}
}
} }
} };
} if (displayLog)
private int CoreStartViaString(string configStr)
{
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
try
{ {
var coreInfo = LazyConfig.Instance.GetCoreInfo(ECoreType.Xray); proc.OutputDataReceived += (sender, e) =>
string fileName = CoreFindexe(coreInfo);
if (fileName == "") return -1;
Process p = new()
{
StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = "-config stdin:",
WorkingDirectory = Utils.GetConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
}
};
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
string msg = e.Data + Environment.NewLine;
ShowMsg(false, msg);
}
};
p.ErrorDataReceived += (sender, e) =>
{ {
if (!string.IsNullOrEmpty(e.Data)) if (!string.IsNullOrEmpty(e.Data))
{ {
string msg = e.Data + Environment.NewLine; string msg = e.Data + Environment.NewLine;
ShowMsg(false, msg); update(false, msg);
} }
}; };
p.Start(); proc.ErrorDataReceived += (sender, e) =>
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.StandardInput.Write(configStr);
p.StandardInput.Close();
if (p.WaitForExit(1000))
{ {
throw new Exception(p.StandardError.ReadToEnd()); if (!string.IsNullOrEmpty(e.Data))
}
Global.processJob.AddProcess(p.Handle);
return p.Id;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
string msg = ex.Message;
ShowMsg(false, msg);
return -1;
}
}
private void ShowMsg(bool updateToTrayTooltip, string msg)
{
_updateFunc(updateToTrayTooltip, msg);
}
#region Process
private Process? RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog, Action<bool, string> update)
{
try
{
string fileName = CoreFindexe(coreInfo);
if (Utils.IsNullOrEmpty(fileName))
{
return null;
}
Process proc = new()
{
StartInfo = new ProcessStartInfo
{ {
FileName = fileName, string msg = e.Data + Environment.NewLine;
Arguments = string.Format(coreInfo.arguments, configPath), update(false, msg);
WorkingDirectory = Utils.GetConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
} }
}; };
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
string msg = e.Data + Environment.NewLine;
update(false, msg);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
string msg = e.Data + Environment.NewLine;
update(false, msg);
}
};
}
proc.Start();
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
if (proc.WaitForExit(1000))
{
throw new Exception(displayLog ? proc.StandardError.ReadToEnd() : "启动进程失败并退出 (Failed to start the process and exited)");
}
Global.processJob.AddProcess(proc.Handle);
return proc;
} }
catch (Exception ex) proc.Start();
if (displayLog)
{ {
Utils.SaveLog(ex.Message, ex); proc.BeginOutputReadLine();
string msg = ex.Message; proc.BeginErrorReadLine();
update(true, msg);
return null;
} }
}
private void KillProcess(Process p) if (proc.WaitForExit(1000))
{
throw new Exception(displayLog ? proc.StandardError.ReadToEnd() : "启动进程失败并退出 (Failed to start the process and exited)");
}
Global.processJob.AddProcess(proc.Handle);
return proc;
}
catch (Exception ex)
{ {
try Utils.SaveLog(ex.Message, ex);
{ string msg = ex.Message;
p.CloseMainWindow(); update(true, msg);
p.WaitForExit(100); return null;
if (!p.HasExited)
{
p.Kill();
p.WaitForExit(100);
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
#endregion Process
} }
private void KillProcess(Process p)
{
try
{
p.CloseMainWindow();
p.WaitForExit(100);
if (!p.HasExited)
{
p.Kill();
p.WaitForExit(100);
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
#endregion Process
} }

View file

@ -7,272 +7,266 @@ using System.Net.Sockets;
using v2rayN.Base; using v2rayN.Base;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
/// <summary>
///Download
/// </summary>
internal class DownloadHandle
{ {
/// <summary> public event EventHandler<ResultEventArgs>? UpdateCompleted;
///Download
/// </summary> public event ErrorEventHandler? Error;
internal class DownloadHandle
public class ResultEventArgs : EventArgs
{ {
public event EventHandler<ResultEventArgs>? UpdateCompleted; public bool Success;
public string Msg;
public event ErrorEventHandler? Error; public ResultEventArgs(bool success, string msg)
public class ResultEventArgs : EventArgs
{ {
public bool Success; Success = success;
public string Msg; Msg = msg;
public ResultEventArgs(bool success, string msg)
{
Success = success;
Msg = msg;
}
} }
}
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> update) public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> update)
{ {
try try
{
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
var progress = new Progress<string>();
progress.ProgressChanged += (sender, value) =>
{
if (update != null)
{
string msg = $"{value}";
update(false, msg);
}
};
await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy,
url,
progress,
downloadTimeout);
}
catch (Exception ex)
{
update(false, ex.Message);
if (ex.InnerException != null)
{
update(false, ex.InnerException.Message);
}
}
return 0;
}
public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
{
try
{
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}"));
var progress = new Progress<double>();
progress.ProgressChanged += (sender, value) =>
{
UpdateCompleted?.Invoke(this, new ResultEventArgs(value > 100, $"...{value}%"));
};
var webProxy = GetWebProxy(blProxy);
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
url,
Utils.GetTempPath(Utils.GetDownloadFileName(url)),
progress,
downloadTimeout);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
}
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
{ {
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
var webRequestHandler = new SocketsHttpHandler
var progress = new Progress<string>();
progress.ProgressChanged += (sender, value) =>
{ {
AllowAutoRedirect = false, if (update != null)
Proxy = GetWebProxy(blProxy) {
string msg = $"{value}";
update(false, msg);
}
}; };
HttpClient client = new(webRequestHandler);
HttpResponseMessage response = await client.GetAsync(url); await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy,
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null) url,
progress,
downloadTimeout);
}
catch (Exception ex)
{
update(false, ex.Message);
if (ex.InnerException != null)
{ {
return response.Headers.Location.ToString(); update(false, ex.InnerException.Message);
}
else
{
Utils.SaveLog("StatusCode error: " + url);
return null;
} }
} }
return 0;
}
public async Task<string?> TryDownloadString(string url, bool blProxy, string userAgent) public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
{
try
{ {
try Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
{ UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}"));
var result1 = await DownloadStringAsync(url, blProxy, userAgent);
if (!Utils.IsNullOrEmpty(result1))
{
return result1;
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
try var progress = new Progress<double>();
progress.ProgressChanged += (sender, value) =>
{ {
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent); UpdateCompleted?.Invoke(this, new ResultEventArgs(value > 100, $"...{value}%"));
if (!Utils.IsNullOrEmpty(result2)) };
{
return result2;
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
try var webProxy = GetWebProxy(blProxy);
{ await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
using var wc = new WebClient(); url,
wc.Proxy = GetWebProxy(blProxy); Utils.GetTempPath(Utils.GetDownloadFileName(url)),
var result3 = await wc.DownloadStringTaskAsync(url); progress,
if (!Utils.IsNullOrEmpty(result3)) downloadTimeout);
{ }
return result3; catch (Exception ex)
} {
} Utils.SaveLog(ex.Message, ex);
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
}
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
{
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
var webRequestHandler = new SocketsHttpHandler
{
AllowAutoRedirect = false,
Proxy = GetWebProxy(blProxy)
};
HttpClient client = new(webRequestHandler);
HttpResponseMessage response = await client.GetAsync(url);
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null)
{
return response.Headers.Location.ToString();
}
else
{
Utils.SaveLog("StatusCode error: " + url);
return null; return null;
} }
}
/// <summary> public async Task<string?> TryDownloadString(string url, bool blProxy, string userAgent)
/// DownloadString {
/// </summary> try
/// <param name="url"></param>
public async Task<string?> DownloadStringAsync(string url, bool blProxy, string userAgent)
{ {
try var result1 = await DownloadStringAsync(url, blProxy, userAgent);
if (!Utils.IsNullOrEmpty(result1))
{ {
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); return result1;
var webProxy = GetWebProxy(blProxy);
var client = new HttpClient(new SocketsHttpHandler()
{
Proxy = webProxy,
UseProxy = webProxy != null
});
if (Utils.IsNullOrEmpty(userAgent))
{
userAgent = Utils.GetVersion(false);
}
client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent);
Uri uri = new(url);
//Authorization Header
if (!Utils.IsNullOrEmpty(uri.UserInfo))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
}
using var cts = new CancellationTokenSource();
var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(30), cts.Token);
return result;
} }
catch (Exception ex) }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{ {
Utils.SaveLog(ex.Message, ex); Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
} }
return null;
} }
/// <summary> try
/// DownloadString
/// </summary>
/// <param name="url"></param>
public async Task<string?> DownloadStringViaDownloader(string url, bool blProxy, string userAgent)
{ {
try var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
if (!Utils.IsNullOrEmpty(result2))
{ {
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); return result2;
var webProxy = GetWebProxy(blProxy);
if (Utils.IsNullOrEmpty(userAgent))
{
userAgent = Utils.GetVersion(false);
}
var result = await DownloaderHelper.Instance.DownloadStringAsync(webProxy, url, userAgent, 30);
return result;
} }
catch (Exception ex) }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{ {
Utils.SaveLog(ex.Message, ex); Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
} }
return null;
} }
public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy) try
{ {
using var wc = new WebClient();
wc.Proxy = GetWebProxy(blProxy);
var result3 = await wc.DownloadStringTaskAsync(url);
if (!Utils.IsNullOrEmpty(result3))
{
return result3;
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
return null;
}
/// <summary>
/// DownloadString
/// </summary>
/// <param name="url"></param>
public async Task<string?> DownloadStringAsync(string url, bool blProxy, string userAgent)
{
try
{
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
var webProxy = GetWebProxy(blProxy);
var client = new HttpClient(new SocketsHttpHandler()
{
Proxy = webProxy,
UseProxy = webProxy != null
});
if (Utils.IsNullOrEmpty(userAgent))
{
userAgent = Utils.GetVersion(false);
}
client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent);
Uri uri = new(url);
//Authorization Header
if (!Utils.IsNullOrEmpty(uri.UserInfo))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
}
using var cts = new CancellationTokenSource();
var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(30), cts.Token);
return result;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
return null;
}
/// <summary>
/// DownloadString
/// </summary>
/// <param name="url"></param>
public async Task<string?> DownloadStringViaDownloader(string url, bool blProxy, string userAgent)
{
try
{
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
var webProxy = GetWebProxy(blProxy);
if (Utils.IsNullOrEmpty(userAgent))
{
userAgent = Utils.GetVersion(false);
}
var result = await DownloaderHelper.Instance.DownloadStringAsync(webProxy, url, userAgent, 30);
return result;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
Error?.Invoke(this, new ErrorEventArgs(ex));
if (ex.InnerException != null)
{
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
}
}
return null;
}
public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy)
{
try
{
if (webProxy == null)
{
webProxy = GetWebProxy(true);
}
try try
{ {
if (webProxy == null) var config = LazyConfig.Instance.GetConfig();
{ int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10);
webProxy = GetWebProxy(true); return responseTime;
}
try
{
var config = LazyConfig.Instance.GetConfig();
int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10);
return responseTime;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return -1;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -280,60 +274,65 @@ namespace v2rayN.Handler
return -1; return -1;
} }
} }
catch (Exception ex)
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
{ {
int responseTime = -1; Utils.SaveLog(ex.Message, ex);
try return -1;
{ }
Stopwatch timer = Stopwatch.StartNew(); }
using var cts = new CancellationTokenSource(); public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout)); {
using var client = new HttpClient(new SocketsHttpHandler() int responseTime = -1;
{ try
Proxy = webProxy, {
UseProxy = webProxy != null Stopwatch timer = Stopwatch.StartNew();
});
await client.GetAsync(url, cts.Token);
responseTime = timer.Elapsed.Milliseconds; using var cts = new CancellationTokenSource();
} cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
catch (Exception ex) using var client = new HttpClient(new SocketsHttpHandler()
{ {
//Utils.SaveLog(ex.Message, ex); Proxy = webProxy,
} UseProxy = webProxy != null
return responseTime; });
await client.GetAsync(url, cts.Token);
responseTime = timer.Elapsed.Milliseconds;
}
catch (Exception ex)
{
//Utils.SaveLog(ex.Message, ex);
}
return responseTime;
}
private WebProxy? GetWebProxy(bool blProxy)
{
if (!blProxy)
{
return null;
}
var httpPort = LazyConfig.Instance.GetLocalPort(Global.InboundHttp);
if (!SocketCheck(Global.Loopback, httpPort))
{
return null;
} }
private WebProxy? GetWebProxy(bool blProxy) return new WebProxy(Global.Loopback, httpPort);
{ }
if (!blProxy)
{
return null;
}
var httpPort = LazyConfig.Instance.GetLocalPort(Global.InboundHttp);
if (!SocketCheck(Global.Loopback, httpPort))
{
return null;
}
return new WebProxy(Global.Loopback, httpPort); private bool SocketCheck(string ip, int port)
{
try
{
IPEndPoint point = new(IPAddress.Parse(ip), port);
using Socket? sock = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(point);
return true;
} }
catch (Exception)
private bool SocketCheck(string ip, int port)
{ {
try return false;
{
IPEndPoint point = new(IPAddress.Parse(ip), port);
using Socket? sock = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(point);
return true;
}
catch (Exception)
{
return false;
}
} }
} }
} }

View file

@ -7,174 +7,173 @@ using System.Windows.Interop;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
public sealed class HotkeyHandler
{ {
public sealed class HotkeyHandler private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
public static HotkeyHandler Instance = _instance.Value;
private const int WmHotkey = 0x0312;
private Config _config
{ {
private static readonly Lazy<HotkeyHandler> _instance = new(() => new()); get => LazyConfig.Instance.GetConfig();
public static HotkeyHandler Instance = _instance.Value; }
private const int WmHotkey = 0x0312; private Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic;
private Config _config public bool IsPause { get; set; } = false;
public event Action<bool, string>? UpdateViewEvent;
public event Action<EGlobalHotkey>? HotkeyTriggerEvent;
public HotkeyHandler()
{
_hotkeyTriggerDic = new();
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
Init();
}
private void Init()
{
_hotkeyTriggerDic.Clear();
if (_config.globalHotkeys == null) return;
foreach (var item in _config.globalHotkeys)
{ {
get => LazyConfig.Instance.GetConfig(); if (item.KeyCode != null && item.KeyCode != Key.None)
}
private Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic;
public bool IsPause { get; set; } = false;
public event Action<bool, string>? UpdateViewEvent;
public event Action<EGlobalHotkey>? HotkeyTriggerEvent;
public HotkeyHandler()
{
_hotkeyTriggerDic = new();
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
Init();
}
private void Init()
{
_hotkeyTriggerDic.Clear();
if (_config.globalHotkeys == null) return;
foreach (var item in _config.globalHotkeys)
{ {
if (item.KeyCode != null && item.KeyCode != Key.None) int key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode);
KeyModifiers modifiers = KeyModifiers.None;
if (item.Control) modifiers |= KeyModifiers.Ctrl;
if (item.Shift) modifiers |= KeyModifiers.Shift;
if (item.Alt) modifiers |= KeyModifiers.Alt;
key = (key << 16) | (int)modifiers;
if (!_hotkeyTriggerDic.ContainsKey(key))
{ {
int key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode); _hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey });
KeyModifiers modifiers = KeyModifiers.None; }
if (item.Control) modifiers |= KeyModifiers.Ctrl; else
if (item.Shift) modifiers |= KeyModifiers.Shift; {
if (item.Alt) modifiers |= KeyModifiers.Alt; if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey))
key = (key << 16) | (int)modifiers; _hotkeyTriggerDic[key].Add(item.eGlobalHotkey);
if (!_hotkeyTriggerDic.ContainsKey(key))
{
_hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey });
}
else
{
if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey))
_hotkeyTriggerDic[key].Add(item.eGlobalHotkey);
}
} }
} }
} }
}
public void Load()
{ public void Load()
foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys) {
{ foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys)
var hotkeyInfo = GetHotkeyInfo(_hotkeyCode); {
bool isSuccess = false; var hotkeyInfo = GetHotkeyInfo(_hotkeyCode);
string msg; bool isSuccess = false;
string msg;
Application.Current.Dispatcher.Invoke(() =>
{ Application.Current.Dispatcher.Invoke(() =>
isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey); {
}); isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey);
foreach (var name in hotkeyInfo.Names) });
{ foreach (var name in hotkeyInfo.Names)
if (isSuccess) {
{ if (isSuccess)
msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})"); {
} msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})");
else }
{ else
var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message; {
msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo); var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message;
} msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo);
UpdateViewEvent?.Invoke(false, msg); }
} UpdateViewEvent?.Invoke(false, msg);
} }
} }
}
public void ReLoad()
{ public void ReLoad()
foreach (var hotkey in _hotkeyTriggerDic.Keys) {
{ foreach (var hotkey in _hotkeyTriggerDic.Keys)
Application.Current.Dispatcher.Invoke(() => {
{ Application.Current.Dispatcher.Invoke(() =>
UnregisterHotKey(IntPtr.Zero, hotkey); {
}); UnregisterHotKey(IntPtr.Zero, hotkey);
} });
Init(); }
Load(); Init();
} Load();
}
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeycode)
{ private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeycode)
var _fsModifiers = hotkeycode & 0xffff; {
var _vkey = (hotkeycode >> 16) & 0xffff; var _fsModifiers = hotkeycode & 0xffff;
var _hotkeyStr = new StringBuilder(); var _vkey = (hotkeycode >> 16) & 0xffff;
var _names = new List<string>(); var _hotkeyStr = new StringBuilder();
var _names = new List<string>();
var mdif = (KeyModifiers)_fsModifiers;
var key = KeyInterop.KeyFromVirtualKey(_vkey); var mdif = (KeyModifiers)_fsModifiers;
if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+"); var key = KeyInterop.KeyFromVirtualKey(_vkey);
if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+"); if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+");
if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+"); if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+");
_hotkeyStr.Append(key.ToString()); if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+");
_hotkeyStr.Append(key.ToString());
foreach (var name in _hotkeyTriggerDic[hotkeycode])
{ foreach (var name in _hotkeyTriggerDic[hotkeycode])
_names.Add(name.ToString()); {
} _names.Add(name.ToString());
}
return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names);
} return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names);
}
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
{ private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam)) {
{ if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam))
return; {
} return;
handled = true; }
var _hotKeyCode = (int)msg.lParam; handled = true;
if (IsPause) var _hotKeyCode = (int)msg.lParam;
{ if (IsPause)
Application.Current.Dispatcher.Invoke(() => {
{ Application.Current.Dispatcher.Invoke(() =>
UIElement? element = Keyboard.FocusedElement as UIElement; {
if (element != null) UIElement? element = Keyboard.FocusedElement as UIElement;
{ if (element != null)
var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, {
PresentationSource.FromVisual(element), 0, var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice,
KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey)) PresentationSource.FromVisual(element), 0,
{ KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey))
RoutedEvent = UIElement.KeyDownEvent {
}; RoutedEvent = UIElement.KeyDownEvent
element.RaiseEvent(_keyEventArgs); };
} element.RaiseEvent(_keyEventArgs);
}); }
} });
else }
{ else
foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam]) {
{ foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam])
HotkeyTriggerEvent?.Invoke(keyEvent); {
} HotkeyTriggerEvent?.Invoke(keyEvent);
} }
} }
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); [DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id); [DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[Flags]
private enum KeyModifiers [Flags]
{ private enum KeyModifiers
None = 0x0000, {
Alt = 0x0001, None = 0x0000,
Ctrl = 0x0002, Alt = 0x0001,
Shift = 0x0004, Ctrl = 0x0002,
Win = 0x0008, Shift = 0x0004,
NoRepeat = 0x4000 Win = 0x0008,
} NoRepeat = 0x4000
} }
} }

View file

@ -1,368 +1,367 @@
using v2rayN.Base; using v2rayN.Base;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler;
public sealed class LazyConfig
{ {
public sealed class LazyConfig private static readonly Lazy<LazyConfig> _instance = new(() => new());
private Config _config;
private List<CoreInfo> coreInfos;
public static LazyConfig Instance => _instance.Value;
public LazyConfig()
{ {
private static readonly Lazy<LazyConfig> _instance = new(() => new()); SqliteHelper.Instance.CreateTable<SubItem>();
private Config _config; SqliteHelper.Instance.CreateTable<ProfileItem>();
private List<CoreInfo> coreInfos; SqliteHelper.Instance.CreateTable<ServerStatItem>();
SqliteHelper.Instance.CreateTable<RoutingItem>();
SqliteHelper.Instance.CreateTable<ProfileExItem>();
SqliteHelper.Instance.CreateTable<DNSItem>();
}
public static LazyConfig Instance => _instance.Value; #region Config
public LazyConfig() public void SetConfig(Config config)
{
_config = config;
}
public Config GetConfig()
{
return _config;
}
public int GetLocalPort(string protocol)
{
int localPort = _config.inbound.FirstOrDefault(t => t.protocol == Global.InboundSocks).localPort;
if (protocol == Global.InboundSocks)
{ {
SqliteHelper.Instance.CreateTable<SubItem>();
SqliteHelper.Instance.CreateTable<ProfileItem>();
SqliteHelper.Instance.CreateTable<ServerStatItem>();
SqliteHelper.Instance.CreateTable<RoutingItem>();
SqliteHelper.Instance.CreateTable<ProfileExItem>();
SqliteHelper.Instance.CreateTable<DNSItem>();
}
#region Config
public void SetConfig(Config config)
{
_config = config;
}
public Config GetConfig()
{
return _config;
}
public int GetLocalPort(string protocol)
{
int localPort = _config.inbound.FirstOrDefault(t => t.protocol == Global.InboundSocks).localPort;
if (protocol == Global.InboundSocks)
{
return localPort;
}
else if (protocol == Global.InboundHttp)
{
return localPort + 1;
}
else if (protocol == Global.InboundSocks2)
{
return localPort + 2;
}
else if (protocol == Global.InboundHttp2)
{
return localPort + 3;
}
else if (protocol == ESysProxyType.Pac.ToString())
{
return localPort + 4;
}
else if (protocol == "speedtest")
{
return localPort + 103;
}
return localPort; return localPort;
} }
else if (protocol == Global.InboundHttp)
public List<SubItem> SubItems()
{ {
return SqliteHelper.Instance.Table<SubItem>().ToList(); return localPort + 1;
} }
else if (protocol == Global.InboundSocks2)
public SubItem GetSubItem(string subid)
{ {
return SqliteHelper.Instance.Table<SubItem>().FirstOrDefault(t => t.id == subid); return localPort + 2;
} }
else if (protocol == Global.InboundHttp2)
public List<ProfileItem> ProfileItems(string subid)
{ {
if (Utils.IsNullOrEmpty(subid)) return localPort + 3;
{
return SqliteHelper.Instance.Table<ProfileItem>().ToList();
}
else
{
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).ToList();
}
} }
else if (protocol == ESysProxyType.Pac.ToString())
public List<string> ProfileItemIndexs(string subid)
{ {
if (Utils.IsNullOrEmpty(subid)) return localPort + 4;
{
return SqliteHelper.Instance.Table<ProfileItem>().Select(t => t.indexId).ToList();
}
else
{
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).Select(t => t.indexId).ToList();
}
} }
else if (protocol == "speedtest")
public List<ProfileItemModel> ProfileItems(string subid, string filter)
{ {
var sql = @$"select a.* return localPort + 103;
}
return localPort;
}
public List<SubItem> SubItems()
{
return SqliteHelper.Instance.Table<SubItem>().ToList();
}
public SubItem GetSubItem(string subid)
{
return SqliteHelper.Instance.Table<SubItem>().FirstOrDefault(t => t.id == subid);
}
public List<ProfileItem> ProfileItems(string subid)
{
if (Utils.IsNullOrEmpty(subid))
{
return SqliteHelper.Instance.Table<ProfileItem>().ToList();
}
else
{
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).ToList();
}
}
public List<string> ProfileItemIndexs(string subid)
{
if (Utils.IsNullOrEmpty(subid))
{
return SqliteHelper.Instance.Table<ProfileItem>().Select(t => t.indexId).ToList();
}
else
{
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).Select(t => t.indexId).ToList();
}
}
public List<ProfileItemModel> ProfileItems(string subid, string filter)
{
var sql = @$"select a.*
,b.remarks subRemarks ,b.remarks subRemarks
from ProfileItem a from ProfileItem a
left join SubItem b on a.subid = b.id left join SubItem b on a.subid = b.id
where 1=1 "; where 1=1 ";
if (!Utils.IsNullOrEmpty(subid)) if (!Utils.IsNullOrEmpty(subid))
{
sql += $" and a.subid = '{subid}'";
}
if (!Utils.IsNullOrEmpty(filter))
{
if (filter.Contains('\''))
{
filter = filter.Replace("'", "");
}
sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
}
return SqliteHelper.Instance.Query<ProfileItemModel>(sql).ToList();
}
public ProfileItem? GetProfileItem(string indexId)
{ {
if (Utils.IsNullOrEmpty(indexId)) sql += $" and a.subid = '{subid}'";
}
if (!Utils.IsNullOrEmpty(filter))
{
if (filter.Contains('\''))
{ {
return null; filter = filter.Replace("'", "");
} }
return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId); sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
} }
public List<RoutingItem> RoutingItems() return SqliteHelper.Instance.Query<ProfileItemModel>(sql).ToList();
{
return SqliteHelper.Instance.Table<RoutingItem>().Where(it => it.locked == false).OrderBy(t => t.sort).ToList();
}
public RoutingItem GetRoutingItem(string id)
{
return SqliteHelper.Instance.Table<RoutingItem>().FirstOrDefault(it => it.locked == false && it.id == id);
}
public List<DNSItem> DNSItems()
{
return SqliteHelper.Instance.Table<DNSItem>().ToList();
}
public DNSItem GetDNSItem(ECoreType eCoreType)
{
return SqliteHelper.Instance.Table<DNSItem>().FirstOrDefault(it => it.coreType == eCoreType);
}
#endregion Config
#region Core Type
public List<string> GetShadowsocksSecuritys(ProfileItem profileItem)
{
if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.v2fly)
{
return Global.ssSecuritys;
}
if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.Xray)
{
return Global.ssSecuritysInXray;
}
return Global.ssSecuritysInSagerNet;
}
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
{
if (profileItem?.coreType != null)
{
return (ECoreType)profileItem.coreType;
}
if (_config.coreTypeItem == null)
{
return ECoreType.Xray;
}
var item = _config.coreTypeItem.FirstOrDefault(it => it.configType == eConfigType);
if (item == null)
{
return ECoreType.Xray;
}
return item.coreType;
}
public CoreInfo? GetCoreInfo(ECoreType coreType)
{
if (coreInfos == null)
{
InitCoreInfo();
}
return coreInfos!.FirstOrDefault(t => t.coreType == coreType);
}
public List<CoreInfo>? GetCoreInfos()
{
if (coreInfos == null)
{
InitCoreInfo();
}
return coreInfos;
}
private void InitCoreInfo()
{
coreInfos = new(16);
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2rayN,
coreUrl = Global.NUrl,
coreReleaseApiUrl = Global.NUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.NUrl + "/download/{0}/v2rayN-32.zip",
coreDownloadUrl64 = Global.NUrl + "/download/{0}/v2rayN.zip",
coreDownloadUrlArm64 = Global.NUrl + "/download/{0}/v2rayN-arm64.zip"
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2fly,
coreExes = new List<string> { "wv2ray", "v2ray" },
arguments = "",
coreUrl = Global.v2flyCoreUrl,
coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "-version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.SagerNet,
coreExes = new List<string> { "SagerNet", "v2ray" },
arguments = "run",
coreUrl = Global.SagerNetCoreUrl,
coreReleaseApiUrl = Global.SagerNetCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2fly_v5,
coreExes = new List<string> { "v2ray" },
arguments = "run",
coreUrl = Global.v2flyCoreUrl,
coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.Xray,
coreExes = new List<string> { "xray", "wxray" },
arguments = "",
coreUrl = Global.xrayCoreUrl,
coreReleaseApiUrl = Global.xrayCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
coreDownloadUrl64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
match = "Xray",
versionArg = "-version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.clash,
coreExes = new List<string> { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" },
arguments = "-f config.json",
coreUrl = Global.clashCoreUrl,
coreReleaseApiUrl = Global.clashCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip",
coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip",
coreDownloadUrlArm64 = Global.clashCoreUrl + "/download/{0}/clash-windows-arm64-{0}.zip",
match = "v",
versionArg = "-v",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.clash_meta,
coreExes = new List<string> { "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" },
arguments = "-f config.json",
coreUrl = Global.clashMetaCoreUrl,
coreReleaseApiUrl = Global.clashMetaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-386-{0}.zip",
coreDownloadUrl64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-amd64-compatible-{0}.zip",
coreDownloadUrlArm64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-arm64-{0}.zip",
match = "v",
versionArg = "-v",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.hysteria,
coreExes = new List<string> { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" },
arguments = "",
coreUrl = Global.hysteriaCoreUrl,
coreReleaseApiUrl = Global.hysteriaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-386.exe",
coreDownloadUrl64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-amd64.exe",
coreDownloadUrlArm64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-arm64.exe",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.naiveproxy,
coreExes = new List<string> { "naiveproxy", "naive" },
arguments = "config.json",
coreUrl = Global.naiveproxyCoreUrl,
redirectInfo = false,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.tuic,
coreExes = new List<string> { "tuic-client", "tuic" },
arguments = "-c config.json",
coreUrl = Global.tuicCoreUrl,
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.sing_box,
coreExes = new List<string> { "sing-box-client", "sing-box" },
arguments = "run{0}",
coreUrl = Global.singboxCoreUrl,
redirectInfo = true,
coreReleaseApiUrl = Global.singboxCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-386.zip",
coreDownloadUrl64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
coreDownloadUrlArm64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
match = "sing-box",
versionArg = "version",
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.juicity,
coreExes = new List<string> { "juicity-client", "juicity" },
arguments = "run -c config.json",
coreUrl = Global.juicityCoreUrl
});
}
#endregion Core Type
} }
public ProfileItem? GetProfileItem(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
{
return null;
}
return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId);
}
public List<RoutingItem> RoutingItems()
{
return SqliteHelper.Instance.Table<RoutingItem>().Where(it => it.locked == false).OrderBy(t => t.sort).ToList();
}
public RoutingItem GetRoutingItem(string id)
{
return SqliteHelper.Instance.Table<RoutingItem>().FirstOrDefault(it => it.locked == false && it.id == id);
}
public List<DNSItem> DNSItems()
{
return SqliteHelper.Instance.Table<DNSItem>().ToList();
}
public DNSItem GetDNSItem(ECoreType eCoreType)
{
return SqliteHelper.Instance.Table<DNSItem>().FirstOrDefault(it => it.coreType == eCoreType);
}
#endregion Config
#region Core Type
public List<string> GetShadowsocksSecuritys(ProfileItem profileItem)
{
if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.v2fly)
{
return Global.ssSecuritys;
}
if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.Xray)
{
return Global.ssSecuritysInXray;
}
return Global.ssSecuritysInSagerNet;
}
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
{
if (profileItem?.coreType != null)
{
return (ECoreType)profileItem.coreType;
}
if (_config.coreTypeItem == null)
{
return ECoreType.Xray;
}
var item = _config.coreTypeItem.FirstOrDefault(it => it.configType == eConfigType);
if (item == null)
{
return ECoreType.Xray;
}
return item.coreType;
}
public CoreInfo? GetCoreInfo(ECoreType coreType)
{
if (coreInfos == null)
{
InitCoreInfo();
}
return coreInfos!.FirstOrDefault(t => t.coreType == coreType);
}
public List<CoreInfo>? GetCoreInfos()
{
if (coreInfos == null)
{
InitCoreInfo();
}
return coreInfos;
}
private void InitCoreInfo()
{
coreInfos = new(16);
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2rayN,
coreUrl = Global.NUrl,
coreReleaseApiUrl = Global.NUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.NUrl + "/download/{0}/v2rayN-32.zip",
coreDownloadUrl64 = Global.NUrl + "/download/{0}/v2rayN.zip",
coreDownloadUrlArm64 = Global.NUrl + "/download/{0}/v2rayN-arm64.zip"
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2fly,
coreExes = new List<string> { "wv2ray", "v2ray" },
arguments = "",
coreUrl = Global.v2flyCoreUrl,
coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "-version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.SagerNet,
coreExes = new List<string> { "SagerNet", "v2ray" },
arguments = "run",
coreUrl = Global.SagerNetCoreUrl,
coreReleaseApiUrl = Global.SagerNetCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.v2fly_v5,
coreExes = new List<string> { "v2ray" },
arguments = "run",
coreUrl = Global.v2flyCoreUrl,
coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip",
match = "V2Ray",
versionArg = "version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.Xray,
coreExes = new List<string> { "xray", "wxray" },
arguments = "",
coreUrl = Global.xrayCoreUrl,
coreReleaseApiUrl = Global.xrayCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
coreDownloadUrl64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
coreDownloadUrlArm64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip",
match = "Xray",
versionArg = "-version",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.clash,
coreExes = new List<string> { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" },
arguments = "-f config.json",
coreUrl = Global.clashCoreUrl,
coreReleaseApiUrl = Global.clashCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip",
coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip",
coreDownloadUrlArm64 = Global.clashCoreUrl + "/download/{0}/clash-windows-arm64-{0}.zip",
match = "v",
versionArg = "-v",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.clash_meta,
coreExes = new List<string> { "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" },
arguments = "-f config.json",
coreUrl = Global.clashMetaCoreUrl,
coreReleaseApiUrl = Global.clashMetaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-386-{0}.zip",
coreDownloadUrl64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-amd64-compatible-{0}.zip",
coreDownloadUrlArm64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-arm64-{0}.zip",
match = "v",
versionArg = "-v",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.hysteria,
coreExes = new List<string> { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" },
arguments = "",
coreUrl = Global.hysteriaCoreUrl,
coreReleaseApiUrl = Global.hysteriaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-386.exe",
coreDownloadUrl64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-amd64.exe",
coreDownloadUrlArm64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-arm64.exe",
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.naiveproxy,
coreExes = new List<string> { "naiveproxy", "naive" },
arguments = "config.json",
coreUrl = Global.naiveproxyCoreUrl,
redirectInfo = false,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.tuic,
coreExes = new List<string> { "tuic-client", "tuic" },
arguments = "-c config.json",
coreUrl = Global.tuicCoreUrl,
redirectInfo = true,
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.sing_box,
coreExes = new List<string> { "sing-box-client", "sing-box" },
arguments = "run{0}",
coreUrl = Global.singboxCoreUrl,
redirectInfo = true,
coreReleaseApiUrl = Global.singboxCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl),
coreDownloadUrl32 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-386.zip",
coreDownloadUrl64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
coreDownloadUrlArm64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
match = "sing-box",
versionArg = "version",
});
coreInfos.Add(new CoreInfo
{
coreType = ECoreType.juicity,
coreExes = new List<string> { "juicity-client", "juicity" },
arguments = "run -c config.json",
coreUrl = Global.juicityCoreUrl
});
}
#endregion Core Type
} }

View file

@ -5,224 +5,223 @@ using System.Windows.Media.Imaging;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
public sealed class MainFormHandler
{ {
public sealed class MainFormHandler private static readonly Lazy<MainFormHandler> instance = new(() => new());
public static MainFormHandler Instance => instance.Value;
public Icon GetNotifyIcon(Config config)
{ {
private static readonly Lazy<MainFormHandler> instance = new(() => new()); try
public static MainFormHandler Instance => instance.Value;
public Icon GetNotifyIcon(Config config)
{ {
try int index = (int)config.sysProxyType;
//Load from routing setting
var createdIcon = GetNotifyIcon4Routing(config);
if (createdIcon != null)
{ {
int index = (int)config.sysProxyType;
//Load from routing setting
var createdIcon = GetNotifyIcon4Routing(config);
if (createdIcon != null)
{
return createdIcon;
}
//Load from local file
var fileName = Utils.GetPath($"NotifyIcon{index + 1}.ico");
if (File.Exists(fileName))
{
return new Icon(fileName);
}
return index switch
{
0 => Properties.Resources.NotifyIcon1,
1 => Properties.Resources.NotifyIcon2,
2 => Properties.Resources.NotifyIcon3,
3 => Properties.Resources.NotifyIcon2,
_ => Properties.Resources.NotifyIcon1, // default
};
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return Properties.Resources.NotifyIcon1;
}
}
public System.Windows.Media.ImageSource GetAppIcon(Config config)
{
int index = 1;
switch ((int)config.sysProxyType)
{
case 0:
index = 1;
break;
case 1:
case 3:
index = 2;
break;
case 2:
index = 3;
break;
}
return BitmapFrame.Create(new Uri($"pack://application:,,,/Resources/NotifyIcon{index}.ico", UriKind.RelativeOrAbsolute));
}
private Icon? GetNotifyIcon4Routing(Config config)
{
try
{
if (!config.routingBasicItem.enableRoutingAdvanced)
{
return null;
}
var item = ConfigHandler.GetDefaultRouting(ref config);
if (item == null || Utils.IsNullOrEmpty(item.customIcon) || !File.Exists(item.customIcon))
{
return null;
}
Color color = ColorTranslator.FromHtml("#3399CC");
int index = (int)config.sysProxyType;
if (index > 0)
{
color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1];
}
int width = 128;
int height = 128;
Bitmap bitmap = new(width, height);
Graphics graphics = Graphics.FromImage(bitmap);
SolidBrush drawBrush = new(color);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//graphics.FillRectangle(drawBrush, new Rectangle(0, 0, width, height));
graphics.DrawImage(new Bitmap(item.customIcon), 0, 0, width, height);
graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2);
Icon createdIcon = Icon.FromHandle(bitmap.GetHicon());
drawBrush.Dispose();
graphics.Dispose();
bitmap.Dispose();
return createdIcon; return createdIcon;
} }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return null;
}
}
public void Export2ClientConfig(ProfileItem item, Config config) //Load from local file
{ var fileName = Utils.GetPath($"NotifyIcon{index + 1}.ico");
if (item == null) if (File.Exists(fileName))
{ {
return; return new Icon(fileName);
} }
if (item.configType == EConfigType.Custom) return index switch
{ {
UI.Show(ResUI.NonVmessService); 0 => Properties.Resources.NotifyIcon1,
return; 1 => Properties.Resources.NotifyIcon2,
} 2 => Properties.Resources.NotifyIcon3,
3 => Properties.Resources.NotifyIcon2,
SaveFileDialog fileDialog = new() _ => Properties.Resources.NotifyIcon1, // default
{
Filter = "Config|*.json",
FilterIndex = 2,
RestoreDirectory = true
}; };
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
if (CoreConfigHandler.GenerateClientConfig(item, fileName, out string msg, out string content) != 0)
{
UI.Show(msg);
}
else
{
UI.ShowWarning(string.Format(ResUI.SaveClientConfigurationIn, fileName));
}
} }
catch (Exception ex)
public void UpdateTask(Config config, Action<bool, string> update)
{ {
Task.Run(() => UpdateTaskRunSubscription(config, update)); Utils.SaveLog(ex.Message, ex);
Task.Run(() => UpdateTaskRunGeo(config, update)); return Properties.Resources.NotifyIcon1;
}
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> update)
{
await Task.Delay(60000);
Utils.SaveLog("UpdateTaskRunSubscription");
var updateHandle = new UpdateHandle();
while (true)
{
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = LazyConfig.Instance.SubItems()
.Where(t => t.autoUpdateInterval > 0)
.Where(t => updateTime - t.updateTime >= t.autoUpdateInterval * 60)
.ToList();
foreach (var item in lstSubs)
{
updateHandle.UpdateSubscriptionProcess(config, item.id, true, (bool success, string msg) =>
{
update(success, msg);
if (success)
Utils.SaveLog("subscription" + msg);
});
item.updateTime = updateTime;
ConfigHandler.AddSubItem(ref config, item);
await Task.Delay(5000);
}
await Task.Delay(60000);
}
}
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> update)
{
var autoUpdateGeoTime = DateTime.Now;
await Task.Delay(1000 * 120);
Utils.SaveLog("UpdateTaskRunGeo");
var updateHandle = new UpdateHandle();
while (true)
{
var dtNow = DateTime.Now;
if (config.guiItem.autoUpdateInterval > 0)
{
if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0)
{
updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
update(false, msg);
});
autoUpdateGeoTime = dtNow;
}
}
await Task.Delay(1000 * 3600);
}
}
public void RegisterGlobalHotkey(Config config, Action<EGlobalHotkey> handler, Action<bool, string> update)
{
HotkeyHandler.Instance.UpdateViewEvent += update;
HotkeyHandler.Instance.HotkeyTriggerEvent += handler;
HotkeyHandler.Instance.Load();
} }
} }
public System.Windows.Media.ImageSource GetAppIcon(Config config)
{
int index = 1;
switch ((int)config.sysProxyType)
{
case 0:
index = 1;
break;
case 1:
case 3:
index = 2;
break;
case 2:
index = 3;
break;
}
return BitmapFrame.Create(new Uri($"pack://application:,,,/Resources/NotifyIcon{index}.ico", UriKind.RelativeOrAbsolute));
}
private Icon? GetNotifyIcon4Routing(Config config)
{
try
{
if (!config.routingBasicItem.enableRoutingAdvanced)
{
return null;
}
var item = ConfigHandler.GetDefaultRouting(ref config);
if (item == null || Utils.IsNullOrEmpty(item.customIcon) || !File.Exists(item.customIcon))
{
return null;
}
Color color = ColorTranslator.FromHtml("#3399CC");
int index = (int)config.sysProxyType;
if (index > 0)
{
color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1];
}
int width = 128;
int height = 128;
Bitmap bitmap = new(width, height);
Graphics graphics = Graphics.FromImage(bitmap);
SolidBrush drawBrush = new(color);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//graphics.FillRectangle(drawBrush, new Rectangle(0, 0, width, height));
graphics.DrawImage(new Bitmap(item.customIcon), 0, 0, width, height);
graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2);
Icon createdIcon = Icon.FromHandle(bitmap.GetHicon());
drawBrush.Dispose();
graphics.Dispose();
bitmap.Dispose();
return createdIcon;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return null;
}
}
public void Export2ClientConfig(ProfileItem item, Config config)
{
if (item == null)
{
return;
}
if (item.configType == EConfigType.Custom)
{
UI.Show(ResUI.NonVmessService);
return;
}
SaveFileDialog fileDialog = new()
{
Filter = "Config|*.json",
FilterIndex = 2,
RestoreDirectory = true
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
if (CoreConfigHandler.GenerateClientConfig(item, fileName, out string msg, out string content) != 0)
{
UI.Show(msg);
}
else
{
UI.ShowWarning(string.Format(ResUI.SaveClientConfigurationIn, fileName));
}
}
public void UpdateTask(Config config, Action<bool, string> update)
{
Task.Run(() => UpdateTaskRunSubscription(config, update));
Task.Run(() => UpdateTaskRunGeo(config, update));
}
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> update)
{
await Task.Delay(60000);
Utils.SaveLog("UpdateTaskRunSubscription");
var updateHandle = new UpdateHandle();
while (true)
{
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = LazyConfig.Instance.SubItems()
.Where(t => t.autoUpdateInterval > 0)
.Where(t => updateTime - t.updateTime >= t.autoUpdateInterval * 60)
.ToList();
foreach (var item in lstSubs)
{
updateHandle.UpdateSubscriptionProcess(config, item.id, true, (bool success, string msg) =>
{
update(success, msg);
if (success)
Utils.SaveLog("subscription" + msg);
});
item.updateTime = updateTime;
ConfigHandler.AddSubItem(ref config, item);
await Task.Delay(5000);
}
await Task.Delay(60000);
}
}
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> update)
{
var autoUpdateGeoTime = DateTime.Now;
await Task.Delay(1000 * 120);
Utils.SaveLog("UpdateTaskRunGeo");
var updateHandle = new UpdateHandle();
while (true)
{
var dtNow = DateTime.Now;
if (config.guiItem.autoUpdateInterval > 0)
{
if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0)
{
updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
update(false, msg);
});
autoUpdateGeoTime = dtNow;
}
}
await Task.Delay(1000 * 3600);
}
}
public void RegisterGlobalHotkey(Config config, Action<EGlobalHotkey> handler, Action<bool, string> update)
{
HotkeyHandler.Instance.UpdateViewEvent += update;
HotkeyHandler.Instance.HotkeyTriggerEvent += handler;
HotkeyHandler.Instance.Load();
}
} }

View file

@ -1,33 +1,32 @@
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
namespace v2rayN.Handler namespace v2rayN.Handler;
public class NoticeHandler
{ {
public class NoticeHandler private readonly ISnackbarMessageQueue _snackbarMessageQueue;
public NoticeHandler(ISnackbarMessageQueue snackbarMessageQueue)
{ {
private readonly ISnackbarMessageQueue _snackbarMessageQueue; _snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue));
public NoticeHandler(ISnackbarMessageQueue snackbarMessageQueue) //_snackbarMessageQueue = snackbarMessageQueue;
{ }
_snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue));
//_snackbarMessageQueue = snackbarMessageQueue; public void Enqueue(object content)
} {
_snackbarMessageQueue?.Enqueue(content);
}
public void Enqueue(object content) public void SendMessage(string msg)
{ {
_snackbarMessageQueue?.Enqueue(content); MessageBus.Current.SendMessage(msg, "MsgView");
} }
public void SendMessage(string msg) public void SendMessage(string msg, bool time)
{ {
MessageBus.Current.SendMessage(msg, "MsgView"); msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {msg}";
} MessageBus.Current.SendMessage(msg, "MsgView");
public void SendMessage(string msg, bool time)
{
msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {msg}";
MessageBus.Current.SendMessage(msg, "MsgView");
}
} }
} }

View file

@ -3,143 +3,142 @@ using System.Reactive.Linq;
using v2rayN.Base; using v2rayN.Base;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler;
internal class ProfileExHandler
{ {
internal class ProfileExHandler private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx;
private Queue<string> _queIndexIds = new();
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
public static ProfileExHandler Instance => _instance.Value;
public ProfileExHandler()
{ {
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); Init();
private ConcurrentBag<ProfileExItem> _lstProfileEx; }
private Queue<string> _queIndexIds = new();
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
public static ProfileExHandler Instance => _instance.Value;
public ProfileExHandler() private void Init()
{
SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
_lstProfileEx = new(SqliteHelper.Instance.Table<ProfileExItem>());
Task.Run(async () =>
{ {
Init(); while (true)
}
private void Init()
{
SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
_lstProfileEx = new(SqliteHelper.Instance.Table<ProfileExItem>());
Task.Run(async () =>
{ {
while (true) var cnt = _queIndexIds.Count;
for (int i = 0; i < cnt; i++)
{ {
var cnt = _queIndexIds.Count; var id = _queIndexIds.Dequeue();
for (int i = 0; i < cnt; i++) var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
if (item is not null)
{ {
var id = _queIndexIds.Dequeue(); SqliteHelper.Instance.Replace(item);
var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
if (item is not null)
{
SqliteHelper.Instance.Replace(item);
}
} }
await Task.Delay(1000 * 60);
} }
}); await Task.Delay(1000 * 60);
}
private void IndexIdEnqueue(string indexId)
{
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
{
_queIndexIds.Enqueue(indexId);
} }
} });
}
private void AddProfileEx(string indexId, ref ProfileExItem profileEx) private void IndexIdEnqueue(string indexId)
{
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
{ {
profileEx = new() _queIndexIds.Enqueue(indexId);
{
indexId = indexId,
delay = 0,
speed = 0,
sort = 0
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
}
public void ClearAll()
{
SqliteHelper.Instance.Execute($"delete from ProfileExItem ");
_lstProfileEx = new();
}
public void SaveTo()
{
try
{
//foreach (var item in _lstProfileEx)
//{
// SqliteHelper.Instance.Replace(item);
//}
SqliteHelper.Instance.UpdateAll(_lstProfileEx);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
public void SetTestDelay(string indexId, string delayVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
int.TryParse(delayVal, out int delay);
profileEx.delay = delay;
IndexIdEnqueue(indexId);
}
public void SetTestSpeed(string indexId, string speedVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
decimal.TryParse(speedVal, out decimal speed);
profileEx.speed = speed;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
profileEx.sort = sort;
IndexIdEnqueue(indexId);
}
public int GetSort(string indexId)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
return 0;
}
return profileEx.sort;
}
public int GetMaxSort()
{
if (_lstProfileEx.Count <= 0)
{
return 0;
}
return _lstProfileEx.Max(t => t == null ? 0 : t.sort);
} }
} }
private void AddProfileEx(string indexId, ref ProfileExItem profileEx)
{
profileEx = new()
{
indexId = indexId,
delay = 0,
speed = 0,
sort = 0
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
}
public void ClearAll()
{
SqliteHelper.Instance.Execute($"delete from ProfileExItem ");
_lstProfileEx = new();
}
public void SaveTo()
{
try
{
//foreach (var item in _lstProfileEx)
//{
// SqliteHelper.Instance.Replace(item);
//}
SqliteHelper.Instance.UpdateAll(_lstProfileEx);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
public void SetTestDelay(string indexId, string delayVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
int.TryParse(delayVal, out int delay);
profileEx.delay = delay;
IndexIdEnqueue(indexId);
}
public void SetTestSpeed(string indexId, string speedVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
decimal.TryParse(speedVal, out decimal speed);
profileEx.speed = speed;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
profileEx.sort = sort;
IndexIdEnqueue(indexId);
}
public int GetSort(string indexId)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
if (profileEx == null)
{
return 0;
}
return profileEx.sort;
}
public int GetMaxSort()
{
if (_lstProfileEx.Count <= 0)
{
return 0;
}
return _lstProfileEx.Max(t => t == null ? 0 : t.sort);
}
} }

View file

@ -1,216 +1,215 @@
using Microsoft.Win32; using Microsoft.Win32;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace v2rayN.Handler namespace v2rayN.Handler;
internal class ProxySetting
{ {
internal class ProxySetting public static bool UnsetProxy()
{ {
public static bool UnsetProxy() return SetProxy(null, null, 1);
}
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
InternetPerConnOptionList list = new();
int optionCount = 1;
if (type == 1)
{ {
return SetProxy(null, null, 1); optionCount = 1;
}
else if (type is 2 or 4)
{
optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3;
} }
public static bool SetProxy(string? strProxy, string? exceptions, int type) int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2)
{ {
InternetPerConnOptionList list = new(); m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
int optionCount = 1; }
if (type == 1) else if (type == 4)
{ {
optionCount = 1; m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
} m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
else if (type is 2 or 4)
{
optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2)
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
}
else if (type == 4)
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
//int optionCount = Utils.IsNullOrEmpty(strProxy) ? 1 : (Utils.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int;
// use THIS proxy server
if (optionCount > 1)
{
options[1].m_Option = m_Option;
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
// except for these addresses ...
if (optionCount > 2)
{
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
list.szConnection = IntPtr.Zero;
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
{
if (Environment.Is64BitOperatingSystem)
{
IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize);
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize) ? -1 : 0;
if (returnvalue == 0)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastWin32Error();
}
// FREE the data ASAP
Marshal.FreeCoTaskMem(optionsPtr);
Marshal.FreeCoTaskMem(ipcoListPtr);
if (returnvalue > 0)
{ // throw the error codes, they might be helpful
//throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (returnvalue < 0);
} }
#region WinInet structures //int optionCount = Utils.IsNullOrEmpty(strProxy) ? 1 : (Utils.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] // USE a proxy server ...
public struct InternetPerConnOptionList options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int;
// use THIS proxy server
if (optionCount > 1)
{ {
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct options[1].m_Option = m_Option;
public IntPtr szConnection; // connection name to set/query options options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
public int dwOptionCount; // number of options to set/query // except for these addresses ...
public int dwOptionError; // on error, which option failed if (optionCount > 2)
//[MarshalAs(UnmanagedType.)]
public IntPtr options;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
{
private static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
static InternetConnectionOption()
{ {
Size = Marshal.SizeOf(typeof(InternetConnectionOption)); options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
} options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
// Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public IntPtr m_StringPtr;
} }
} }
#endregion WinInet structures // default stuff
list.dwSize = Marshal.SizeOf(list);
list.szConnection = IntPtr.Zero;
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
#region WinInet enums int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
// IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
// options manifests for Internet{Query|Set}Option // copy the array over into that spot in memory ...
// for (int i = 0; i < options.Length; ++i)
public enum InternetOption : uint
{ {
INTERNET_OPTION_PER_CONNECTION_OPTION = 75 if (Environment.Is64BitOperatingSystem)
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
#endregion WinInet enums
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
}
//判断是否使用代理
public static bool UsedProxy()
{
using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (rk?.GetValue("ProxyEnable")?.ToString() == "1")
{ {
return true; IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
} }
else else
{ {
return false; IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
} }
} }
//获得代理的IP和端口 list.options = optionsPtr;
public static string? GetProxyProxyServer()
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize);
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize) ? -1 : 0;
if (returnvalue == 0)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastWin32Error();
}
// FREE the data ASAP
Marshal.FreeCoTaskMem(optionsPtr);
Marshal.FreeCoTaskMem(ipcoListPtr);
if (returnvalue > 0)
{ // throw the error codes, they might be helpful
//throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (returnvalue < 0);
}
#region WinInet structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetPerConnOptionList
{
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
public IntPtr szConnection; // connection name to set/query options
public int dwOptionCount; // number of options to set/query
public int dwOptionError; // on error, which option failed
//[MarshalAs(UnmanagedType.)]
public IntPtr options;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
{
private static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
static InternetConnectionOption()
{ {
using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true); Size = Marshal.SizeOf(typeof(InternetConnectionOption));
string ProxyServer = rk.GetValue("ProxyServer").ToString(); }
return ProxyServer;
// Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public IntPtr m_StringPtr;
} }
} }
#endregion WinInet structures
#region WinInet enums
//
// options manifests for Internet{Query|Set}Option
//
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
#endregion WinInet enums
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
}
//判断是否使用代理
public static bool UsedProxy()
{
using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (rk?.GetValue("ProxyEnable")?.ToString() == "1")
{
return true;
}
else
{
return false;
}
}
//获得代理的IP和端口
public static string? GetProxyProxyServer()
{
using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
string ProxyServer = rk.GetValue("ProxyServer").ToString();
return ProxyServer;
}
} }

View file

@ -2,27 +2,22 @@
using QRCoder.Xaml; using QRCoder.Xaml;
using System.Windows.Media; using System.Windows.Media;
namespace v2rayN.Handler namespace v2rayN.Handler;
public class QRCodeHelper
{ {
/// <summary> public static DrawingImage? GetQRCode(string strContent)
/// 含有QR码的描述类和包装编码和渲染
/// </summary>
public class QRCodeHelper
{ {
public static DrawingImage? GetQRCode(string strContent) try
{ {
try QRCodeGenerator qrGenerator = new();
{ QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H);
QRCodeGenerator qrGenerator = new(); XamlQRCode qrCode = new(qrCodeData);
QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H); DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40);
XamlQRCode qrCode = new(qrCodeData); return qrCodeAsXaml;
DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40); }
return qrCodeAsXaml; catch
} {
catch return null;
{
return null;
}
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -5,423 +5,422 @@ using System.Net.Sockets;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler;
{
internal class SpeedtestHandler
{
private Config _config;
private CoreHandler _coreHandler;
private List<ServerTestItem> _selecteds;
private ESpeedActionType _actionType;
private Action<string, string, string> _updateFunc;
public SpeedtestHandler(Config config) internal class SpeedtestHandler
{
private Config _config;
private CoreHandler _coreHandler;
private List<ServerTestItem> _selecteds;
private ESpeedActionType _actionType;
private Action<string, string, string> _updateFunc;
public SpeedtestHandler(Config config)
{
_config = config;
}
public SpeedtestHandler(Config config, CoreHandler coreHandler, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<string, string, string> update)
{
_config = config;
_coreHandler = coreHandler;
_actionType = actionType;
_updateFunc = update;
_selecteds = new List<ServerTestItem>();
foreach (var it in selecteds)
{ {
_config = config; if (it.configType == EConfigType.Custom)
{
continue;
}
if (it.port <= 0)
{
continue;
}
_selecteds.Add(new ServerTestItem()
{
indexId = it.indexId,
address = it.address,
port = it.port,
configType = it.configType
});
}
//clear test result
foreach (var it in _selecteds)
{
switch (actionType)
{
case ESpeedActionType.Ping:
case ESpeedActionType.Tcping:
case ESpeedActionType.Realping:
UpdateFunc(it.indexId, ResUI.Speedtesting, "");
ProfileExHandler.Instance.SetTestDelay(it.indexId, "0");
break;
case ESpeedActionType.Speedtest:
UpdateFunc(it.indexId, "", ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0");
break;
case ESpeedActionType.Mixedtest:
UpdateFunc(it.indexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestDelay(it.indexId, "0");
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0");
break;
}
} }
public SpeedtestHandler(Config config, CoreHandler coreHandler, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<string, string, string> update) switch (actionType)
{ {
_config = config; case ESpeedActionType.Ping:
_coreHandler = coreHandler; Task.Run(RunPing);
_actionType = actionType; break;
_updateFunc = update;
_selecteds = new List<ServerTestItem>(); case ESpeedActionType.Tcping:
foreach (var it in selecteds) Task.Run(RunTcping);
break;
case ESpeedActionType.Realping:
Task.Run(RunRealPing);
break;
case ESpeedActionType.Speedtest:
Task.Run(RunSpeedTestAsync);
break;
case ESpeedActionType.Mixedtest:
Task.Run(RunMixedtestAsync);
break;
}
}
private async Task RunPingSubAsync(Action<ServerTestItem> updateFun)
{
try
{
foreach (var it in _selecteds.Where(it => it.configType != EConfigType.Custom))
{ {
try
{
Task.Run(() => updateFun(it));
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
await Task.Delay(10);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private async void RunPing()
{
await RunPingSubAsync((ServerTestItem it) =>
{
long time = Ping(it.address);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
});
}
private async void RunTcping()
{
await RunPingSubAsync((ServerTestItem it) =>
{
int time = GetTcpingTime(it.address, it.port);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
});
}
private Task RunRealPing()
{
int pid = -1;
try
{
string msg = string.Empty;
pid = _coreHandler.LoadCoreConfigString(_selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return Task.CompletedTask;
}
DownloadHandle downloadHandle = new DownloadHandle();
List<Task> tasks = new();
foreach (var it in _selecteds)
{
if (!it.allowTest)
{
continue;
}
if (it.configType == EConfigType.Custom) if (it.configType == EConfigType.Custom)
{ {
continue; continue;
} }
if (it.port <= 0) tasks.Add(Task.Run(async () =>
{
continue;
}
_selecteds.Add(new ServerTestItem()
{
indexId = it.indexId,
address = it.address,
port = it.port,
configType = it.configType
});
}
//clear test result
foreach (var it in _selecteds)
{
switch (actionType)
{
case ESpeedActionType.Ping:
case ESpeedActionType.Tcping:
case ESpeedActionType.Realping:
UpdateFunc(it.indexId, ResUI.Speedtesting, "");
ProfileExHandler.Instance.SetTestDelay(it.indexId, "0");
break;
case ESpeedActionType.Speedtest:
UpdateFunc(it.indexId, "", ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0");
break;
case ESpeedActionType.Mixedtest:
UpdateFunc(it.indexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestDelay(it.indexId, "0");
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0");
break;
}
}
switch (actionType)
{
case ESpeedActionType.Ping:
Task.Run(RunPing);
break;
case ESpeedActionType.Tcping:
Task.Run(RunTcping);
break;
case ESpeedActionType.Realping:
Task.Run(RunRealPing);
break;
case ESpeedActionType.Speedtest:
Task.Run(RunSpeedTestAsync);
break;
case ESpeedActionType.Mixedtest:
Task.Run(RunMixedtestAsync);
break;
}
}
private async Task RunPingSubAsync(Action<ServerTestItem> updateFun)
{
try
{
foreach (var it in _selecteds.Where(it => it.configType != EConfigType.Custom))
{ {
try try
{ {
Task.Run(() => updateFun(it)); WebProxy webProxy = new(Global.Loopback, it.port);
string output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
int.TryParse(output, out int delay);
it.delay = delay;
} }
catch (Exception ex) catch (Exception ex)
{ {
Utils.SaveLog(ex.Message, ex); Utils.SaveLog(ex.Message, ex);
} }
}));
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
finally
{
if (pid > 0) _coreHandler.CoreStopPid(pid);
ProfileExHandler.Instance.SaveTo();
}
return Task.CompletedTask;
}
private async Task RunSpeedTestAsync()
{
int pid = -1;
//if (_actionType == ESpeedActionType.Mixedtest)
//{
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
//}
pid = _coreHandler.LoadCoreConfigString(_selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
string url = _config.speedTestItem.speedTestUrl;
var timeout = _config.speedTestItem.speedTestTimeout;
DownloadHandle downloadHandle = new();
foreach (var it in _selecteds)
{
if (!it.allowTest)
{
continue;
}
if (it.configType == EConfigType.Custom)
{
continue;
}
//if (it.delay < 0)
//{
// UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
// continue;
//}
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1");
UpdateFunc(it.indexId, "", ResUI.Speedtesting);
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.port);
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) =>
{
decimal.TryParse(msg, out decimal dec);
if (dec > 0)
{
ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg);
} }
UpdateFunc(it.indexId, "", msg);
await Task.Delay(10);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private async void RunPing()
{
await RunPingSubAsync((ServerTestItem it) =>
{
long time = Ping(it.address);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
});
}
private async void RunTcping()
{
await RunPingSubAsync((ServerTestItem it) =>
{
int time = GetTcpingTime(it.address, it.port);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
}); });
} }
private Task RunRealPing() if (pid > 0)
{ {
int pid = -1; _coreHandler.CoreStopPid(pid);
try }
UpdateFunc("", ResUI.SpeedtestingCompleted);
ProfileExHandler.Instance.SaveTo();
}
private async Task RunSpeedTestMulti()
{
int pid = -1;
pid = _coreHandler.LoadCoreConfigString(_selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
string url = _config.speedTestItem.speedTestUrl;
var timeout = _config.speedTestItem.speedTestTimeout;
DownloadHandle downloadHandle = new();
foreach (var it in _selecteds)
{
if (!it.allowTest)
{ {
string msg = string.Empty; continue;
}
if (it.configType == EConfigType.Custom)
{
continue;
}
if (it.delay < 0)
{
UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
continue;
}
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1");
UpdateFunc(it.indexId, "", ResUI.Speedtesting);
pid = _coreHandler.LoadCoreConfigString(_selecteds); var item = LazyConfig.Instance.GetProfileItem(it.indexId);
if (pid < 0) if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.port);
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) =>
{
decimal.TryParse(msg, out decimal dec);
if (dec > 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg);
return Task.CompletedTask;
} }
UpdateFunc(it.indexId, "", msg);
});
await Task.Delay(2000);
}
DownloadHandle downloadHandle = new DownloadHandle(); await Task.Delay((timeout + 2) * 1000);
List<Task> tasks = new(); if (pid > 0)
foreach (var it in _selecteds) {
_coreHandler.CoreStopPid(pid);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
ProfileExHandler.Instance.SaveTo();
}
private async Task RunMixedtestAsync()
{
await RunRealPing();
await Task.Delay(1000);
await RunSpeedTestMulti();
}
public async Task<string> GetRealPingTime(DownloadHandle downloadHandle, IWebProxy webProxy)
{
int responseTime = await downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10);
//string output = Utils.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
return FormatOut(responseTime, Global.DelayUnit);
}
private int GetTcpingTime(string url, int port)
{
int responseTime = -1;
try
{
if (!IPAddress.TryParse(url, out IPAddress ipAddress))
{
IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(url);
ipAddress = ipHostInfo.AddressList[0];
}
Stopwatch timer = new();
timer.Start();
IPEndPoint endPoint = new(ipAddress, port);
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null);
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
throw new TimeoutException("connect timeout (5s): " + url);
clientSocket.EndConnect(result);
timer.Stop();
responseTime = timer.Elapsed.Milliseconds;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
return responseTime;
}
/// <summary>
/// Ping
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public long Ping(string host)
{
long roundtripTime = -1;
try
{
int timeout = 30;
int echoNum = 2;
using Ping pingSender = new();
for (int i = 0; i < echoNum; i++)
{
PingReply reply = pingSender.Send(host, timeout);
if (reply.Status == IPStatus.Success)
{ {
if (!it.allowTest) if (reply.RoundtripTime < 0)
{ {
continue; continue;
} }
if (it.configType == EConfigType.Custom) if (roundtripTime < 0 || reply.RoundtripTime < roundtripTime)
{ {
continue; roundtripTime = reply.RoundtripTime;
}
tasks.Add(Task.Run(async () =>
{
try
{
WebProxy webProxy = new(Global.Loopback, it.port);
string output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.indexId, output);
UpdateFunc(it.indexId, output);
int.TryParse(output, out int delay);
it.delay = delay;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}));
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
finally
{
if (pid > 0) _coreHandler.CoreStopPid(pid);
ProfileExHandler.Instance.SaveTo();
}
return Task.CompletedTask;
}
private async Task RunSpeedTestAsync()
{
int pid = -1;
//if (_actionType == ESpeedActionType.Mixedtest)
//{
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
//}
pid = _coreHandler.LoadCoreConfigString(_selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
string url = _config.speedTestItem.speedTestUrl;
var timeout = _config.speedTestItem.speedTestTimeout;
DownloadHandle downloadHandle = new();
foreach (var it in _selecteds)
{
if (!it.allowTest)
{
continue;
}
if (it.configType == EConfigType.Custom)
{
continue;
}
//if (it.delay < 0)
//{
// UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
// continue;
//}
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1");
UpdateFunc(it.indexId, "", ResUI.Speedtesting);
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.port);
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) =>
{
decimal.TryParse(msg, out decimal dec);
if (dec > 0)
{
ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg);
}
UpdateFunc(it.indexId, "", msg);
});
}
if (pid > 0)
{
_coreHandler.CoreStopPid(pid);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
ProfileExHandler.Instance.SaveTo();
}
private async Task RunSpeedTestMulti()
{
int pid = -1;
pid = _coreHandler.LoadCoreConfigString(_selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
string url = _config.speedTestItem.speedTestUrl;
var timeout = _config.speedTestItem.speedTestTimeout;
DownloadHandle downloadHandle = new();
foreach (var it in _selecteds)
{
if (!it.allowTest)
{
continue;
}
if (it.configType == EConfigType.Custom)
{
continue;
}
if (it.delay < 0)
{
UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
continue;
}
ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1");
UpdateFunc(it.indexId, "", ResUI.Speedtesting);
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.port);
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) =>
{
decimal.TryParse(msg, out decimal dec);
if (dec > 0)
{
ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg);
}
UpdateFunc(it.indexId, "", msg);
});
await Task.Delay(2000);
}
await Task.Delay((timeout + 2) * 1000);
if (pid > 0)
{
_coreHandler.CoreStopPid(pid);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
ProfileExHandler.Instance.SaveTo();
}
private async Task RunMixedtestAsync()
{
await RunRealPing();
await Task.Delay(1000);
await RunSpeedTestMulti();
}
public async Task<string> GetRealPingTime(DownloadHandle downloadHandle, IWebProxy webProxy)
{
int responseTime = await downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10);
//string output = Utils.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
return FormatOut(responseTime, Global.DelayUnit);
}
private int GetTcpingTime(string url, int port)
{
int responseTime = -1;
try
{
if (!IPAddress.TryParse(url, out IPAddress ipAddress))
{
IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(url);
ipAddress = ipHostInfo.AddressList[0];
}
Stopwatch timer = new();
timer.Start();
IPEndPoint endPoint = new(ipAddress, port);
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null);
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
throw new TimeoutException("connect timeout (5s): " + url);
clientSocket.EndConnect(result);
timer.Stop();
responseTime = timer.Elapsed.Milliseconds;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
return responseTime;
}
/// <summary>
/// Ping
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public long Ping(string host)
{
long roundtripTime = -1;
try
{
int timeout = 30;
int echoNum = 2;
using Ping pingSender = new();
for (int i = 0; i < echoNum; i++)
{
PingReply reply = pingSender.Send(host, timeout);
if (reply.Status == IPStatus.Success)
{
if (reply.RoundtripTime < 0)
{
continue;
}
if (roundtripTime < 0 || reply.RoundtripTime < roundtripTime)
{
roundtripTime = reply.RoundtripTime;
}
} }
} }
} }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return -1;
}
return roundtripTime;
} }
catch (Exception ex)
private string FormatOut(object time, string unit)
{ {
//if (time.ToString().Equals("-1")) Utils.SaveLog(ex.Message, ex);
//{ return -1;
// return "Timeout";
//}
return $"{time}";
} }
return roundtripTime;
}
private void UpdateFunc(string indexId, string delay, string speed = "") private string FormatOut(object time, string unit)
{ {
_updateFunc(indexId, delay, speed); //if (time.ToString().Equals("-1"))
} //{
// return "Timeout";
//}
return $"{time}";
}
private void UpdateFunc(string indexId, string delay, string speed = "")
{
_updateFunc(indexId, delay, speed);
} }
} }

View file

@ -3,159 +3,158 @@ using System.Net.Sockets;
using v2rayN.Base; using v2rayN.Base;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler;
internal class StatisticsHandler
{ {
internal class StatisticsHandler private Config _config;
private ServerStatItem? _serverStatItem;
private List<ServerStatItem> _lstServerStat;
private Action<ServerSpeedItem> _updateFunc;
private StatisticsV2ray? _statisticsV2Ray;
private StatisticsSingbox? _statisticsSingbox;
public List<ServerStatItem> ServerStat => _lstServerStat;
public bool Enable { get; set; }
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
{ {
private Config _config; _config = config;
private ServerStatItem? _serverStatItem; Enable = config.guiItem.enableStatistics;
private List<ServerStatItem> _lstServerStat; if (!Enable)
private Action<ServerSpeedItem> _updateFunc;
private StatisticsV2ray? _statisticsV2Ray;
private StatisticsSingbox? _statisticsSingbox;
public List<ServerStatItem> ServerStat => _lstServerStat;
public bool Enable { get; set; }
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
{ {
_config = config; return;
Enable = config.guiItem.enableStatistics;
if (!Enable)
{
return;
}
_updateFunc = update;
Init();
Global.statePort = GetFreePort();
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
} }
public void Close() _updateFunc = update;
Init();
Global.statePort = GetFreePort();
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
}
public void Close()
{
try
{ {
try _statisticsV2Ray?.Close();
{ _statisticsSingbox?.Close();
_statisticsV2Ray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
catch (Exception ex)
public void ClearAllServerStatistics()
{ {
SqliteHelper.Instance.Execute($"delete from ServerStatItem "); Utils.SaveLog(ex.Message, ex);
_serverStatItem = null;
_lstServerStat = new();
}
public void SaveTo()
{
try
{
SqliteHelper.Instance.UpdateAll(_lstServerStat);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private void Init()
{
SqliteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
long ticks = DateTime.Now.Date.Ticks;
SqliteHelper.Instance.Execute($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
_lstServerStat = SqliteHelper.Instance.Table<ServerStatItem>().ToList();
}
private void UpdateServerStat(ServerSpeedItem server)
{
GetServerStatItem(_config.indexId);
if (server.proxyUp != 0 || server.proxyDown != 0)
{
_serverStatItem.todayUp += server.proxyUp;
_serverStatItem.todayDown += server.proxyDown;
_serverStatItem.totalUp += server.proxyUp;
_serverStatItem.totalDown += server.proxyDown;
}
if (Global.ShowInTaskbar)
{
server.indexId = _config.indexId;
server.todayUp = _serverStatItem.todayUp;
server.todayDown = _serverStatItem.todayDown;
server.totalUp = _serverStatItem.totalUp;
server.totalDown = _serverStatItem.totalDown;
_updateFunc(server);
}
}
private void GetServerStatItem(string indexId)
{
long ticks = DateTime.Now.Date.Ticks;
if (_serverStatItem != null && _serverStatItem.indexId != indexId)
{
_serverStatItem = null;
}
if (_serverStatItem == null)
{
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.indexId == indexId);
if (_serverStatItem == null)
{
_serverStatItem = new ServerStatItem
{
indexId = indexId,
totalUp = 0,
totalDown = 0,
todayUp = 0,
todayDown = 0,
dateNow = ticks
};
SqliteHelper.Instance.Replace(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.dateNow != ticks)
{
_serverStatItem.todayUp = 0;
_serverStatItem.todayDown = 0;
_serverStatItem.dateNow = ticks;
}
}
private int GetFreePort()
{
try
{
int defaultPort = 9090;
if (!Utils.PortInUse(defaultPort))
{
return defaultPort;
}
for (int i = 0; i < 3; i++)
{
TcpListener l = new(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
}
catch
{
}
return 69090;
} }
} }
public void ClearAllServerStatistics()
{
SqliteHelper.Instance.Execute($"delete from ServerStatItem ");
_serverStatItem = null;
_lstServerStat = new();
}
public void SaveTo()
{
try
{
SqliteHelper.Instance.UpdateAll(_lstServerStat);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private void Init()
{
SqliteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
long ticks = DateTime.Now.Date.Ticks;
SqliteHelper.Instance.Execute($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
_lstServerStat = SqliteHelper.Instance.Table<ServerStatItem>().ToList();
}
private void UpdateServerStat(ServerSpeedItem server)
{
GetServerStatItem(_config.indexId);
if (server.proxyUp != 0 || server.proxyDown != 0)
{
_serverStatItem.todayUp += server.proxyUp;
_serverStatItem.todayDown += server.proxyDown;
_serverStatItem.totalUp += server.proxyUp;
_serverStatItem.totalDown += server.proxyDown;
}
if (Global.ShowInTaskbar)
{
server.indexId = _config.indexId;
server.todayUp = _serverStatItem.todayUp;
server.todayDown = _serverStatItem.todayDown;
server.totalUp = _serverStatItem.totalUp;
server.totalDown = _serverStatItem.totalDown;
_updateFunc(server);
}
}
private void GetServerStatItem(string indexId)
{
long ticks = DateTime.Now.Date.Ticks;
if (_serverStatItem != null && _serverStatItem.indexId != indexId)
{
_serverStatItem = null;
}
if (_serverStatItem == null)
{
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.indexId == indexId);
if (_serverStatItem == null)
{
_serverStatItem = new ServerStatItem
{
indexId = indexId,
totalUp = 0,
totalDown = 0,
todayUp = 0,
todayDown = 0,
dateNow = ticks
};
SqliteHelper.Instance.Replace(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.dateNow != ticks)
{
_serverStatItem.todayUp = 0;
_serverStatItem.todayDown = 0;
_serverStatItem.dateNow = ticks;
}
}
private int GetFreePort()
{
try
{
int defaultPort = 9090;
if (!Utils.PortInUse(defaultPort))
{
return defaultPort;
}
for (int i = 0; i < 3; i++)
{
TcpListener l = new(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
}
catch
{
}
return 69090;
}
} }

View file

@ -2,127 +2,126 @@
using System.Text; using System.Text;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler;
internal class StatisticsSingbox
{ {
internal class StatisticsSingbox private Config _config;
private bool _exitFlag;
private ClientWebSocket? webSocket;
private string url = string.Empty;
private Action<ServerSpeedItem> _updateFunc;
public StatisticsSingbox(Config config, Action<ServerSpeedItem> update)
{ {
private Config _config; _config = config;
private bool _exitFlag; _updateFunc = update;
private ClientWebSocket? webSocket; _exitFlag = false;
private string url = string.Empty;
private Action<ServerSpeedItem> _updateFunc;
public StatisticsSingbox(Config config, Action<ServerSpeedItem> update) Task.Run(() => Run());
}
private async void Init()
{
await Task.Delay(5000);
try
{ {
_config = config; url = $"ws://{Global.Loopback}:{Global.statePort}/traffic";
_updateFunc = update;
_exitFlag = false;
Task.Run(() => Run()); if (webSocket == null)
}
private async void Init()
{
await Task.Delay(5000);
try
{ {
url = $"ws://{Global.Loopback}:{Global.statePort}/traffic"; webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
if (webSocket == null)
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
}
} }
catch { }
} }
catch { }
}
public void Close() public void Close()
{
try
{
_exitFlag = true;
if (webSocket != null)
{
webSocket.Abort();
webSocket = null;
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private async void Run()
{
Init();
while (!_exitFlag)
{ {
try try
{ {
_exitFlag = true;
if (webSocket != null) if (webSocket != null)
{ {
webSocket.Abort(); if (webSocket.State == WebSocketState.Aborted
webSocket = null; || webSocket.State == WebSocketState.Closed)
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
private async void Run()
{
Init();
while (!_exitFlag)
{
try
{
if (webSocket != null)
{ {
if (webSocket.State == WebSocketState.Aborted webSocket.Abort();
|| webSocket.State == WebSocketState.Closed) webSocket = null;
{ Init();
webSocket.Abort(); continue;
webSocket = null;
Init();
continue;
}
if (webSocket.State != WebSocketState.Open)
{
continue;
}
var buffer = new byte[1024];
var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!res.CloseStatus.HasValue)
{
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
if (!string.IsNullOrEmpty(result))
{
ParseOutput(result, out ulong up, out ulong down);
_updateFunc(new ServerSpeedItem()
{
proxyUp = (long)(up / 1000),
proxyDown = (long)(down / 1000)
});
}
res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
} }
}
catch
{
}
finally
{
await Task.Delay(1000);
}
}
}
private void ParseOutput(string source, out ulong up, out ulong down) if (webSocket.State != WebSocketState.Open)
{ {
up = 0; down = 0; continue;
try }
{
var trafficItem = Utils.FromJson<TrafficItem>(source); var buffer = new byte[1024];
if (trafficItem != null) var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
{ while (!res.CloseStatus.HasValue)
up = trafficItem.up; {
down = trafficItem.down; var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
if (!string.IsNullOrEmpty(result))
{
ParseOutput(result, out ulong up, out ulong down);
_updateFunc(new ServerSpeedItem()
{
proxyUp = (long)(up / 1000),
proxyDown = (long)(down / 1000)
});
}
res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
} }
} }
catch catch
{ {
} }
finally
{
await Task.Delay(1000);
}
}
}
private void ParseOutput(string source, out ulong up, out ulong down)
{
up = 0; down = 0;
try
{
var trafficItem = Utils.FromJson<TrafficItem>(source);
if (trafficItem != null)
{
up = trafficItem.up;
down = trafficItem.down;
}
}
catch
{
} }
} }
} }

View file

@ -3,117 +3,116 @@ using Grpc.Net.Client;
using ProtosLib.Statistics; using ProtosLib.Statistics;
using v2rayN.Mode; using v2rayN.Mode;
namespace v2rayN.Handler namespace v2rayN.Handler;
internal class StatisticsV2ray
{ {
internal class StatisticsV2ray private Mode.Config _config;
private GrpcChannel _channel;
private StatsService.StatsServiceClient _client;
private bool _exitFlag;
private Action<ServerSpeedItem> _updateFunc;
public StatisticsV2ray(Mode.Config config, Action<ServerSpeedItem> update)
{ {
private Mode.Config _config; _config = config;
private GrpcChannel _channel; _updateFunc = update;
private StatsService.StatsServiceClient _client; _exitFlag = false;
private bool _exitFlag;
private Action<ServerSpeedItem> _updateFunc;
public StatisticsV2ray(Mode.Config config, Action<ServerSpeedItem> update) GrpcInit();
Task.Run(Run);
}
private void GrpcInit()
{
if (_channel == null)
{ {
_config = config; _channel = GrpcChannel.ForAddress($"{Global.httpProtocol}{Global.Loopback}:{Global.statePort}");
_updateFunc = update; _client = new StatsService.StatsServiceClient(_channel);
_exitFlag = false;
GrpcInit();
Task.Run(Run);
} }
}
private void GrpcInit() public void Close()
{
_exitFlag = true;
}
private async void Run()
{
while (!_exitFlag)
{ {
if (_channel == null)
{
_channel = GrpcChannel.ForAddress($"{Global.httpProtocol}{Global.Loopback}:{Global.statePort}");
_client = new StatsService.StatsServiceClient(_channel);
}
}
public void Close()
{
_exitFlag = true;
}
private async void Run()
{
while (!_exitFlag)
{
try
{
if (_channel.State == ConnectivityState.Ready)
{
QueryStatsResponse? res = null;
try
{
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
}
catch
{
}
if (res != null)
{
ParseOutput(res.Stat, out ServerSpeedItem server);
_updateFunc(server);
}
}
await Task.Delay(1000);
await _channel.ConnectAsync();
}
catch
{
}
}
}
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
{
server = new();
try try
{ {
foreach (Stat stat in source) if (_channel.State == ConnectivityState.Ready)
{ {
string name = stat.Name; QueryStatsResponse? res = null;
long value = stat.Value / 1024; //KByte try
string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string type = "";
name = name.Trim();
name = nStr[1];
type = nStr[3];
if (name == Global.agentTag)
{ {
if (type == "uplink") res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
{
server.proxyUp = value;
}
else if (type == "downlink")
{
server.proxyDown = value;
}
} }
else if (name == Global.directTag) catch
{ {
if (type == "uplink") }
{
server.directUp = value; if (res != null)
} {
else if (type == "downlink") ParseOutput(res.Stat, out ServerSpeedItem server);
{ _updateFunc(server);
server.directDown = value;
}
} }
} }
await Task.Delay(1000);
await _channel.ConnectAsync();
} }
catch catch
{ {
} }
} }
} }
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
{
server = new();
try
{
foreach (Stat stat in source)
{
string name = stat.Name;
long value = stat.Value / 1024; //KByte
string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string type = "";
name = name.Trim();
name = nStr[1];
type = nStr[3];
if (name == Global.agentTag)
{
if (type == "uplink")
{
server.proxyUp = value;
}
else if (type == "downlink")
{
server.proxyDown = value;
}
}
else if (name == Global.directTag)
{
if (type == "uplink")
{
server.directUp = value;
}
else if (type == "downlink")
{
server.directDown = value;
}
}
}
}
catch
{
}
}
} }

View file

@ -6,217 +6,216 @@ using v2rayN.Mode;
using v2rayN.Properties; using v2rayN.Properties;
using v2rayN.Tool; using v2rayN.Tool;
namespace v2rayN.Handler namespace v2rayN.Handler;
public static class SysProxyHandle
{ {
public static class SysProxyHandle //private const string _userWininetConfigFile = "user-wininet.json";
//private static string _queryStr;
// In general, this won't change
// format:
// <flags><CR-LF>
// <proxy-server><CR-LF>
// <bypass-list><CR-LF>
// <pac-url>
private static SysproxyConfig? _userSettings = null;
private enum RET_ERRORS : int
{ {
//private const string _userWininetConfigFile = "user-wininet.json"; RET_NO_ERROR = 0,
INVALID_FORMAT = 1,
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4,
INVAILD_OPTION_COUNT = 5,
};
//private static string _queryStr; static SysProxyHandle()
{
// In general, this won't change try
// format:
// <flags><CR-LF>
// <proxy-server><CR-LF>
// <bypass-list><CR-LF>
// <pac-url>
private static SysproxyConfig? _userSettings = null;
private enum RET_ERRORS : int
{ {
RET_NO_ERROR = 0, FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
INVALID_FORMAT = 1, Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4,
INVAILD_OPTION_COUNT = 5,
};
static SysProxyHandle()
{
try
{
FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
}
catch (IOException ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
catch (IOException ex)
public static bool UpdateSysProxy(Config config, bool forceDisable)
{ {
var type = config.sysProxyType; Utils.SaveLog(ex.Message, ex);
if (forceDisable && type != ESysProxyType.Unchanged)
{
type = ESysProxyType.ForcedClear;
}
try
{
int port = LazyConfig.Instance.GetLocalPort(Global.InboundHttp);
int portSocks = LazyConfig.Instance.GetLocalPort(Global.InboundSocks);
int portPac = LazyConfig.Instance.GetLocalPort(ESysProxyType.Pac.ToString());
if (port <= 0)
{
return false;
}
if (type == ESysProxyType.ForcedChange)
{
var strExceptions = $"{config.constItem.defIEProxyExceptions};{config.systemProxyExceptions}";
var strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.systemProxyAdvancedProtocol))
{
strProxy = $"{Global.Loopback}:{port}";
}
else
{
strProxy = config.systemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString());
}
ProxySetting.SetProxy(strProxy, strExceptions, 2);
SetIEProxy(true, strProxy, strExceptions);
}
else if (type == ESysProxyType.ForcedClear)
{
ProxySetting.UnsetProxy();
ResetIEProxy();
}
else if (type == ESysProxyType.Unchanged)
{
}
else if (type == ESysProxyType.Pac)
{
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.httpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySetting.SetProxy(strProxy, "", 4);
SetIEProxy(false, strProxy, "");
}
if (type != ESysProxyType.Pac)
{
PacHandler.Stop();
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
return true;
}
public static void ResetIEProxy4WindowsShutDown()
{
try
{
//TODO To be verified
Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0);
}
catch
{
}
}
public static void SetIEProxy(bool global, string strProxy, string strExceptions)
{
string arguments = global
? $"global {strProxy} {strExceptions}"
: $"pac {strProxy}";
ExecSysproxy(arguments);
}
// set system proxy to 1 (null) (null) (null)
public static void ResetIEProxy()
{
ExecSysproxy("set 1 - - -");
}
private static void ExecSysproxy(string arguments)
{
// using event to avoid hanging when redirect standard output/error
// ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why
// and http://blog.csdn.net/zhangweixing0/article/details/7356841
using AutoResetEvent outputWaitHandle = new(false);
using AutoResetEvent errorWaitHandle = new(false);
using Process process = new();
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe");
process.StartInfo.Arguments = arguments;
process.StartInfo.WorkingDirectory = Utils.GetTempPath();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
// Need to provide encoding info, or output/error strings we got will be wrong.
process.StartInfo.StandardOutputEncoding = Encoding.Unicode;
process.StartInfo.StandardErrorEncoding = Encoding.Unicode;
process.StartInfo.CreateNoWindow = true;
StringBuilder output = new(1024);
StringBuilder error = new(1024);
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
output.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorWaitHandle.Set();
}
else
{
error.AppendLine(e.Data);
}
};
try
{
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
}
catch (System.ComponentModel.Win32Exception e)
{
// log the arguments
throw new Exception(process.StartInfo.Arguments);
}
string stderr = error.ToString();
string stdout = output.ToString();
int exitCode = process.ExitCode;
if (exitCode != (int)RET_ERRORS.RET_NO_ERROR)
{
throw new Exception(stderr);
}
//if (arguments == "query")
//{
// if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty())
// {
// throw new Exception("failed to query wininet settings");
// }
// _queryStr = stdout;
//}
} }
} }
public static bool UpdateSysProxy(Config config, bool forceDisable)
{
var type = config.sysProxyType;
if (forceDisable && type != ESysProxyType.Unchanged)
{
type = ESysProxyType.ForcedClear;
}
try
{
int port = LazyConfig.Instance.GetLocalPort(Global.InboundHttp);
int portSocks = LazyConfig.Instance.GetLocalPort(Global.InboundSocks);
int portPac = LazyConfig.Instance.GetLocalPort(ESysProxyType.Pac.ToString());
if (port <= 0)
{
return false;
}
if (type == ESysProxyType.ForcedChange)
{
var strExceptions = $"{config.constItem.defIEProxyExceptions};{config.systemProxyExceptions}";
var strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.systemProxyAdvancedProtocol))
{
strProxy = $"{Global.Loopback}:{port}";
}
else
{
strProxy = config.systemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString());
}
ProxySetting.SetProxy(strProxy, strExceptions, 2);
SetIEProxy(true, strProxy, strExceptions);
}
else if (type == ESysProxyType.ForcedClear)
{
ProxySetting.UnsetProxy();
ResetIEProxy();
}
else if (type == ESysProxyType.Unchanged)
{
}
else if (type == ESysProxyType.Pac)
{
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.httpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySetting.SetProxy(strProxy, "", 4);
SetIEProxy(false, strProxy, "");
}
if (type != ESysProxyType.Pac)
{
PacHandler.Stop();
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
return true;
}
public static void ResetIEProxy4WindowsShutDown()
{
try
{
//TODO To be verified
Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0);
}
catch
{
}
}
public static void SetIEProxy(bool global, string strProxy, string strExceptions)
{
string arguments = global
? $"global {strProxy} {strExceptions}"
: $"pac {strProxy}";
ExecSysproxy(arguments);
}
// set system proxy to 1 (null) (null) (null)
public static void ResetIEProxy()
{
ExecSysproxy("set 1 - - -");
}
private static void ExecSysproxy(string arguments)
{
// using event to avoid hanging when redirect standard output/error
// ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why
// and http://blog.csdn.net/zhangweixing0/article/details/7356841
using AutoResetEvent outputWaitHandle = new(false);
using AutoResetEvent errorWaitHandle = new(false);
using Process process = new();
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe");
process.StartInfo.Arguments = arguments;
process.StartInfo.WorkingDirectory = Utils.GetTempPath();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
// Need to provide encoding info, or output/error strings we got will be wrong.
process.StartInfo.StandardOutputEncoding = Encoding.Unicode;
process.StartInfo.StandardErrorEncoding = Encoding.Unicode;
process.StartInfo.CreateNoWindow = true;
StringBuilder output = new(1024);
StringBuilder error = new(1024);
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
output.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorWaitHandle.Set();
}
else
{
error.AppendLine(e.Data);
}
};
try
{
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
}
catch (System.ComponentModel.Win32Exception e)
{
// log the arguments
throw new Exception(process.StartInfo.Arguments);
}
string stderr = error.ToString();
string stdout = output.ToString();
int exitCode = process.ExitCode;
if (exitCode != (int)RET_ERRORS.RET_NO_ERROR)
{
throw new Exception(stderr);
}
//if (arguments == "query")
//{
// if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty())
// {
// throw new Exception("failed to query wininet settings");
// }
// _queryStr = stdout;
//}
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,14 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
{
public class ComboItem
{
public string ID
{
get; set;
}
public string Text public class ComboItem
{ {
get; set; public string ID
} {
get; set;
}
public string Text
{
get; set;
} }
} }

View file

@ -1,37 +1,36 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
/// <summary>
/// 本软件配置文件实体类
/// </summary>
[Serializable]
public class Config
{ {
/// <summary> #region property
/// 本软件配置文件实体类
/// </summary>
[Serializable]
public class Config
{
#region property
public string indexId { get; set; } public string indexId { get; set; }
public string subIndexId { get; set; } public string subIndexId { get; set; }
public ESysProxyType sysProxyType { get; set; } public ESysProxyType sysProxyType { get; set; }
public string systemProxyExceptions { get; set; } public string systemProxyExceptions { get; set; }
public string systemProxyAdvancedProtocol { get; set; } public string systemProxyAdvancedProtocol { get; set; }
#endregion property #endregion property
#region other entities #region other entities
public CoreBasicItem coreBasicItem { get; set; } public CoreBasicItem coreBasicItem { get; set; }
public TunModeItem tunModeItem { get; set; } public TunModeItem tunModeItem { get; set; }
public KcpItem kcpItem { get; set; } public KcpItem kcpItem { get; set; }
public GrpcItem grpcItem { get; set; } public GrpcItem grpcItem { get; set; }
public RoutingBasicItem routingBasicItem { get; set; } public RoutingBasicItem routingBasicItem { get; set; }
public GUIItem guiItem { get; set; } public GUIItem guiItem { get; set; }
public UIItem uiItem { get; set; } public UIItem uiItem { get; set; }
public ConstItem constItem { get; set; } public ConstItem constItem { get; set; }
public SpeedTestItem speedTestItem { get; set; } public SpeedTestItem speedTestItem { get; set; }
public Mux4Sbox mux4Sbox { get; set; } public Mux4Sbox mux4Sbox { get; set; }
public List<InItem> inbound { get; set; } public List<InItem> inbound { get; set; }
public List<KeyEventItem> globalHotkeys { get; set; } public List<KeyEventItem> globalHotkeys { get; set; }
public List<CoreTypeItem> coreTypeItem { get; set; } public List<CoreTypeItem> coreTypeItem { get; set; }
#endregion other entities #endregion other entities
}
} }

View file

@ -1,209 +1,208 @@
using System.Windows.Input; using System.Windows.Input;
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class CoreBasicItem
{ {
[Serializable] /// <summary>
public class CoreBasicItem /// 允许日志
{ /// </summary>
/// <summary> public bool logEnabled { get; set; }
/// 允许日志
/// </summary>
public bool logEnabled { get; set; }
/// <summary> /// <summary>
/// 日志等级 /// 日志等级
/// </summary> /// </summary>
public string loglevel { get; set; } public string loglevel { get; set; }
/// <summary> /// <summary>
/// 允许Mux多路复用 /// 允许Mux多路复用
/// </summary> /// </summary>
public bool muxEnabled { get; set; } public bool muxEnabled { get; set; }
/// <summary> /// <summary>
/// 是否允许不安全连接 /// 是否允许不安全连接
/// </summary> /// </summary>
public bool defAllowInsecure { get; set; } public bool defAllowInsecure { get; set; }
public string defFingerprint { get; set; } public string defFingerprint { get; set; }
/// <summary> /// <summary>
/// 默认用户代理 /// 默认用户代理
/// </summary> /// </summary>
public string defUserAgent { get; set; } public string defUserAgent { get; set; }
} }
[Serializable] [Serializable]
public class InItem public class InItem
{ {
public int localPort { get; set; } public int localPort { get; set; }
public string protocol { get; set; } public string protocol { get; set; }
public bool udpEnabled { get; set; } public bool udpEnabled { get; set; }
public bool sniffingEnabled { get; set; } = true; public bool sniffingEnabled { get; set; } = true;
public bool routeOnly { get; set; } public bool routeOnly { get; set; }
public bool allowLANConn { get; set; } public bool allowLANConn { get; set; }
public bool newPort4LAN { get; set; } public bool newPort4LAN { get; set; }
public string user { get; set; } public string user { get; set; }
public string pass { get; set; } public string pass { get; set; }
} }
[Serializable] [Serializable]
public class KcpItem public class KcpItem
{ {
public int mtu { get; set; } public int mtu { get; set; }
public int tti { get; set; } public int tti { get; set; }
public int uplinkCapacity { get; set; } public int uplinkCapacity { get; set; }
public int downlinkCapacity { get; set; } public int downlinkCapacity { get; set; }
public bool congestion { get; set; } public bool congestion { get; set; }
public int readBufferSize { get; set; } public int readBufferSize { get; set; }
public int writeBufferSize { get; set; } public int writeBufferSize { get; set; }
} }
[Serializable] [Serializable]
public class GrpcItem public class GrpcItem
{ {
public int idle_timeout { get; set; } public int idle_timeout { get; set; }
public int health_check_timeout { get; set; } public int health_check_timeout { get; set; }
public bool permit_without_stream { get; set; } public bool permit_without_stream { get; set; }
public int initial_windows_size { get; set; } public int initial_windows_size { get; set; }
} }
[Serializable] [Serializable]
public class GUIItem public class GUIItem
{ {
public bool autoRun { get; set; } public bool autoRun { get; set; }
public bool enableStatistics { get; set; } public bool enableStatistics { get; set; }
public bool keepOlderDedupl { get; set; } public bool keepOlderDedupl { get; set; }
public bool ignoreGeoUpdateCore { get; set; } = true; public bool ignoreGeoUpdateCore { get; set; } = true;
public int autoUpdateInterval { get; set; } = 10; public int autoUpdateInterval { get; set; } = 10;
public bool checkPreReleaseUpdate { get; set; } = false; public bool checkPreReleaseUpdate { get; set; } = false;
public bool enableSecurityProtocolTls13 { get; set; } public bool enableSecurityProtocolTls13 { get; set; }
public int trayMenuServersLimit { get; set; } = 20; public int trayMenuServersLimit { get; set; } = 20;
public bool enableHWA { get; set; } = false; public bool enableHWA { get; set; } = false;
public bool enableLog { get; set; } = true; public bool enableLog { get; set; } = true;
} }
[Serializable] [Serializable]
public class UIItem public class UIItem
{ {
public bool enableAutoAdjustMainLvColWidth { get; set; } public bool enableAutoAdjustMainLvColWidth { get; set; }
public double mainWidth { get; set; } public double mainWidth { get; set; }
public double mainHeight { get; set; } public double mainHeight { get; set; }
public double mainGirdHeight1 { get; set; } public double mainGirdHeight1 { get; set; }
public double mainGirdHeight2 { get; set; } public double mainGirdHeight2 { get; set; }
public bool colorModeDark { get; set; } public bool colorModeDark { get; set; }
public bool followSystemTheme { get; set; } public bool followSystemTheme { get; set; }
public string? colorPrimaryName { get; set; } public string? colorPrimaryName { get; set; }
public string currentLanguage { get; set; } public string currentLanguage { get; set; }
public string currentFontFamily { get; set; } public string currentFontFamily { get; set; }
public int currentFontSize { get; set; } public int currentFontSize { get; set; }
public bool enableDragDropSort { get; set; } public bool enableDragDropSort { get; set; }
public bool doubleClick2Activate { get; set; } public bool doubleClick2Activate { get; set; }
public bool autoHideStartup { get; set; } = true; public bool autoHideStartup { get; set; } = true;
public string mainMsgFilter { get; set; } public string mainMsgFilter { get; set; }
public bool showTrayTip { get; set; } public bool showTrayTip { get; set; }
public List<ColumnItem> mainColumnItem { get; set; } public List<ColumnItem> mainColumnItem { get; set; }
} }
[Serializable] [Serializable]
public class ConstItem public class ConstItem
{ {
public string defIEProxyExceptions { get; set; } public string defIEProxyExceptions { get; set; }
public string subConvertUrl { get; set; } = string.Empty; public string subConvertUrl { get; set; } = string.Empty;
} }
[Serializable] [Serializable]
public class KeyEventItem public class KeyEventItem
{ {
public EGlobalHotkey eGlobalHotkey { get; set; } public EGlobalHotkey eGlobalHotkey { get; set; }
public bool Alt { get; set; } public bool Alt { get; set; }
public bool Control { get; set; } public bool Control { get; set; }
public bool Shift { get; set; } public bool Shift { get; set; }
public Key? KeyCode { get; set; } public Key? KeyCode { get; set; }
} }
[Serializable] [Serializable]
public class CoreTypeItem public class CoreTypeItem
{ {
public EConfigType configType { get; set; } public EConfigType configType { get; set; }
public ECoreType coreType { get; set; } public ECoreType coreType { get; set; }
} }
[Serializable] [Serializable]
public class TunModeItem public class TunModeItem
{ {
public bool enableTun { get; set; } public bool enableTun { get; set; }
public bool strictRoute { get; set; } = true; public bool strictRoute { get; set; } = true;
public string stack { get; set; } public string stack { get; set; }
public int mtu { get; set; } public int mtu { get; set; }
public bool enableExInbound { get; set; } public bool enableExInbound { get; set; }
} }
[Serializable] [Serializable]
public class SpeedTestItem public class SpeedTestItem
{ {
public int speedTestTimeout { get; set; } public int speedTestTimeout { get; set; }
public string speedTestUrl { get; set; } public string speedTestUrl { get; set; }
public string speedPingTestUrl { get; set; } public string speedPingTestUrl { get; set; }
} }
[Serializable] [Serializable]
public class RoutingBasicItem public class RoutingBasicItem
{ {
/// <summary> /// <summary>
/// 域名解析策略 /// 域名解析策略
/// </summary> /// </summary>
public string domainStrategy { get; set; } public string domainStrategy { get; set; }
public string domainStrategy4Singbox { get; set; } public string domainStrategy4Singbox { get; set; }
public string domainMatcher { get; set; } public string domainMatcher { get; set; }
public string routingIndexId { get; set; } public string routingIndexId { get; set; }
public bool enableRoutingAdvanced { get; set; } public bool enableRoutingAdvanced { get; set; }
} }
[Serializable] [Serializable]
public class ColumnItem public class ColumnItem
{ {
public string Name { get; set; } public string Name { get; set; }
public int Width { get; set; } public int Width { get; set; }
public int Index { get; set; } public int Index { get; set; }
} }
[Serializable] [Serializable]
public class Mux4Sbox public class Mux4Sbox
{ {
public string protocol { get; set; } public string protocol { get; set; }
public int max_connections { get; set; } public int max_connections { get; set; }
public int min_streams { get; set; } public int min_streams { get; set; }
public int max_streams { get; set; } public int max_streams { get; set; }
public bool padding { get; set; } public bool padding { get; set; }
}
} }

View file

@ -1,440 +1,439 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class ConfigOld
{ {
[Serializable] #region property
public class ConfigOld
/// <summary>
/// 允许日志
/// </summary>
public bool logEnabled
{ {
#region property get; set;
/// <summary>
/// 允许日志
/// </summary>
public bool logEnabled
{
get; set;
}
/// <summary>
/// 日志等级
/// </summary>
public string loglevel
{
get; set;
}
public string indexId
{
get; set;
}
/// <summary>
/// 允许Mux多路复用
/// </summary>
public bool muxEnabled
{
get; set;
}
/// <summary>
///
/// </summary>
public ESysProxyType sysProxyType
{
get; set;
}
/// <summary>
/// 启用实时网速和流量统计
/// </summary>
public bool enableStatistics
{
get; set;
}
/// <summary>
/// 去重时优先保留较旧(顶部)节点
/// </summary>
public bool keepOlderDedupl
{
get; set;
}
/// <summary>
/// 视图刷新率
/// </summary>
public int statisticsFreshRate
{
get; set;
}
/// <summary>
/// 自定义远程DNS
/// </summary>
public string remoteDNS
{
get; set;
}
/// <summary>
/// Outbound Freedom domainStrategy
/// </summary>
public string domainStrategy4Freedom
{
get; set;
}
/// <summary>
/// 是否允许不安全连接
/// </summary>
public bool defAllowInsecure
{
get; set;
}
/// <summary>
/// 域名解析策略
/// </summary>
public string domainStrategy
{
get; set;
}
public string domainMatcher
{
get; set;
}
public int routingIndex
{
get; set;
}
public bool enableRoutingAdvanced
{
get; set;
}
public bool ignoreGeoUpdateCore
{
get; set;
}
/// <summary>
/// systemProxyExceptions
/// </summary>
public string systemProxyExceptions
{
get; set;
}
public string systemProxyAdvancedProtocol { get; set; }
public int autoUpdateInterval { get; set; } = 0;
public int autoUpdateSubInterval { get; set; } = 0;
public bool checkPreReleaseUpdate { get; set; } = false;
public bool enableSecurityProtocolTls13
{
get; set;
}
public int trayMenuServersLimit { get; set; }
#endregion property
#region other entities
/// <summary>
/// 本地监听
/// </summary>
public List<InItem> inbound
{
get; set;
}
/// <summary>
/// vmess服务器信息
/// </summary>
public List<VmessItem> vmess
{
get; set;
}
/// <summary>
/// KcpItem
/// </summary>
public KcpItem kcpItem
{
get; set;
}
/// <summary>
/// 订阅
/// </summary>
public List<SubItem> subItem
{
get; set;
}
/// <summary>
/// UI
/// </summary>
public UIItem uiItem
{
get; set;
}
public List<RoutingItemOld> routings
{
get; set;
}
public ConstItem constItem
{
get; set;
}
public List<KeyEventItem> globalHotkeys
{
get; set;
}
public List<CoreTypeItem> coreTypeItem
{
get; set;
}
#endregion other entities
} }
[Serializable] /// <summary>
public class VmessItem /// 日志等级
/// </summary>
public string loglevel
{ {
public VmessItem() get; set;
{
indexId = string.Empty;
configType = EConfigType.VMess;
configVersion = 2;
sort = 0;
address = string.Empty;
port = 0;
id = string.Empty;
alterId = 0;
security = string.Empty;
network = string.Empty;
remarks = string.Empty;
headerType = string.Empty;
requestHost = string.Empty;
path = string.Empty;
streamSecurity = string.Empty;
allowInsecure = string.Empty;
testResult = string.Empty;
subid = string.Empty;
flow = string.Empty;
groupId = string.Empty;
}
public string indexId
{
get; set;
}
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType
{
get; set;
}
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion
{
get; set;
}
public int sort
{
get; set;
}
/// <summary>
/// 远程服务器地址
/// </summary>
public string address
{
get; set;
}
/// <summary>
/// 远程服务器端口
/// </summary>
public int port
{
get; set;
}
/// <summary>
/// 远程服务器ID
/// </summary>
public string id
{
get; set;
}
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId
{
get; set;
}
/// <summary>
/// 本地安全策略
/// </summary>
public string security
{
get; set;
}
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network
{
get; set;
}
/// <summary>
///
/// </summary>
public string remarks
{
get; set;
}
/// <summary>
/// 伪装类型
/// </summary>
public string headerType
{
get; set;
}
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost
{
get; set;
}
/// <summary>
/// ws h2 path
/// </summary>
public string path
{
get; set;
}
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity
{
get; set;
}
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure
{
get; set;
}
/// <summary>
///
/// </summary>
public string testResult
{
get; set;
}
/// <summary>
/// SubItem id
/// </summary>
public string subid
{
get; set;
}
/// <summary>
/// VLESS flow
/// </summary>
public string flow
{
get; set;
}
/// <summary>
/// tls sni
/// </summary>
public string sni
{
get; set;
}
public string groupId
{
get; set;
} = string.Empty;
public ECoreType? coreType
{
get; set;
}
public int preSocksPort
{
get; set;
}
public string fingerprint { get; set; }
} }
[Serializable] public string indexId
public class RoutingItemOld
{ {
public string remarks get; set;
{ }
get; set;
}
public string url /// <summary>
{ /// 允许Mux多路复用
get; set; /// </summary>
} public bool muxEnabled
{
get; set;
}
public List<RulesItem> rules /// <summary>
{ ///
get; set; /// </summary>
} public ESysProxyType sysProxyType
{
get; set;
}
public bool enabled { get; set; } = true; /// <summary>
/// 启用实时网速和流量统计
/// </summary>
public bool enableStatistics
{
get; set;
}
public bool locked /// <summary>
{ /// 去重时优先保留较旧(顶部)节点
get; set; /// </summary>
} public bool keepOlderDedupl
{
get; set;
}
public string customIcon /// <summary>
{ /// 视图刷新率
get; set; /// </summary>
} public int statisticsFreshRate
{
get; set;
}
/// <summary>
/// 自定义远程DNS
/// </summary>
public string remoteDNS
{
get; set;
}
/// <summary>
/// Outbound Freedom domainStrategy
/// </summary>
public string domainStrategy4Freedom
{
get; set;
}
/// <summary>
/// 是否允许不安全连接
/// </summary>
public bool defAllowInsecure
{
get; set;
}
/// <summary>
/// 域名解析策略
/// </summary>
public string domainStrategy
{
get; set;
}
public string domainMatcher
{
get; set;
}
public int routingIndex
{
get; set;
}
public bool enableRoutingAdvanced
{
get; set;
}
public bool ignoreGeoUpdateCore
{
get; set;
}
/// <summary>
/// systemProxyExceptions
/// </summary>
public string systemProxyExceptions
{
get; set;
}
public string systemProxyAdvancedProtocol { get; set; }
public int autoUpdateInterval { get; set; } = 0;
public int autoUpdateSubInterval { get; set; } = 0;
public bool checkPreReleaseUpdate { get; set; } = false;
public bool enableSecurityProtocolTls13
{
get; set;
}
public int trayMenuServersLimit { get; set; }
#endregion property
#region other entities
/// <summary>
/// 本地监听
/// </summary>
public List<InItem> inbound
{
get; set;
}
/// <summary>
/// vmess服务器信息
/// </summary>
public List<VmessItem> vmess
{
get; set;
}
/// <summary>
/// KcpItem
/// </summary>
public KcpItem kcpItem
{
get; set;
}
/// <summary>
/// 订阅
/// </summary>
public List<SubItem> subItem
{
get; set;
}
/// <summary>
/// UI
/// </summary>
public UIItem uiItem
{
get; set;
}
public List<RoutingItemOld> routings
{
get; set;
}
public ConstItem constItem
{
get; set;
}
public List<KeyEventItem> globalHotkeys
{
get; set;
}
public List<CoreTypeItem> coreTypeItem
{
get; set;
}
#endregion other entities
}
[Serializable]
public class VmessItem
{
public VmessItem()
{
indexId = string.Empty;
configType = EConfigType.VMess;
configVersion = 2;
sort = 0;
address = string.Empty;
port = 0;
id = string.Empty;
alterId = 0;
security = string.Empty;
network = string.Empty;
remarks = string.Empty;
headerType = string.Empty;
requestHost = string.Empty;
path = string.Empty;
streamSecurity = string.Empty;
allowInsecure = string.Empty;
testResult = string.Empty;
subid = string.Empty;
flow = string.Empty;
groupId = string.Empty;
}
public string indexId
{
get; set;
}
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType
{
get; set;
}
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion
{
get; set;
}
public int sort
{
get; set;
}
/// <summary>
/// 远程服务器地址
/// </summary>
public string address
{
get; set;
}
/// <summary>
/// 远程服务器端口
/// </summary>
public int port
{
get; set;
}
/// <summary>
/// 远程服务器ID
/// </summary>
public string id
{
get; set;
}
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId
{
get; set;
}
/// <summary>
/// 本地安全策略
/// </summary>
public string security
{
get; set;
}
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network
{
get; set;
}
/// <summary>
///
/// </summary>
public string remarks
{
get; set;
}
/// <summary>
/// 伪装类型
/// </summary>
public string headerType
{
get; set;
}
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost
{
get; set;
}
/// <summary>
/// ws h2 path
/// </summary>
public string path
{
get; set;
}
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity
{
get; set;
}
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure
{
get; set;
}
/// <summary>
///
/// </summary>
public string testResult
{
get; set;
}
/// <summary>
/// SubItem id
/// </summary>
public string subid
{
get; set;
}
/// <summary>
/// VLESS flow
/// </summary>
public string flow
{
get; set;
}
/// <summary>
/// tls sni
/// </summary>
public string sni
{
get; set;
}
public string groupId
{
get; set;
} = string.Empty;
public ECoreType? coreType
{
get; set;
}
public int preSocksPort
{
get; set;
}
public string fingerprint { get; set; }
}
[Serializable]
public class RoutingItemOld
{
public string remarks
{
get; set;
}
public string url
{
get; set;
}
public List<RulesItem> rules
{
get; set;
}
public bool enabled { get; set; } = true;
public bool locked
{
get; set;
}
public string customIcon
{
get; set;
} }
} }

View file

@ -1,27 +1,26 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class CoreInfo
{ {
[Serializable] public ECoreType coreType { get; set; }
public class CoreInfo
{
public ECoreType coreType { get; set; }
public List<string> coreExes { get; set; } public List<string> coreExes { get; set; }
public string arguments { get; set; } public string arguments { get; set; }
public string coreUrl { get; set; } public string coreUrl { get; set; }
public string coreReleaseApiUrl { get; set; } public string coreReleaseApiUrl { get; set; }
public string coreDownloadUrl32 { get; set; } public string coreDownloadUrl32 { get; set; }
public string coreDownloadUrl64 { get; set; } public string coreDownloadUrl64 { get; set; }
public string coreDownloadUrlArm64 { get; set; } public string coreDownloadUrlArm64 { get; set; }
public string match { get; set; } public string match { get; set; }
public string versionArg { get; set; } public string versionArg { get; set; }
public bool redirectInfo { get; set; } public bool redirectInfo { get; set; }
}
} }

View file

@ -1,18 +1,17 @@
using SQLite; using SQLite;
namespace v2rayN.Mode namespace v2rayN.Mode;
{
[Serializable]
public class DNSItem
{
[PrimaryKey]
public string id { get; set; }
public string remarks { get; set; } [Serializable]
public bool enabled { get; set; } = true; public class DNSItem
public ECoreType coreType { get; set; } {
public string? normalDNS { get; set; } [PrimaryKey]
public string? tunDNS { get; set; } public string id { get; set; }
public string? domainStrategy4Freedom { get; set; }
} public string remarks { get; set; }
public bool enabled { get; set; } = true;
public ECoreType coreType { get; set; }
public string? normalDNS { get; set; }
public string? tunDNS { get; set; }
public string? domainStrategy4Freedom { get; set; }
} }

View file

@ -1,12 +1,11 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum EConfigType
{ {
public enum EConfigType VMess = 1,
{ Custom = 2,
VMess = 1, Shadowsocks = 3,
Custom = 2, Socks = 4,
Shadowsocks = 3, VLESS = 5,
Socks = 4, Trojan = 6
VLESS = 5,
Trojan = 6
}
} }

View file

@ -1,18 +1,17 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum ECoreType
{ {
public enum ECoreType v2fly = 1,
{ Xray = 2,
v2fly = 1, SagerNet = 3,
Xray = 2, v2fly_v5 = 4,
SagerNet = 3, clash = 11,
v2fly_v5 = 4, clash_meta = 12,
clash = 11, hysteria = 21,
clash_meta = 12, naiveproxy = 22,
hysteria = 21, tuic = 23,
naiveproxy = 22, sing_box = 24,
tuic = 23, juicity = 25,
sing_box = 24, v2rayN = 99
juicity = 25,
v2rayN = 99
}
} }

View file

@ -1,11 +1,10 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum EGlobalHotkey
{ {
public enum EGlobalHotkey ShowForm = 0,
{ SystemProxyClear = 1,
ShowForm = 0, SystemProxySet = 2,
SystemProxyClear = 1, SystemProxyUnchanged = 3,
SystemProxySet = 2, SystemProxyPac = 4,
SystemProxyUnchanged = 3,
SystemProxyPac = 4,
}
} }

View file

@ -1,11 +1,10 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum EMove
{ {
public enum EMove Top = 1,
{ Up = 2,
Top = 1, Down = 3,
Up = 2, Bottom = 4,
Down = 3, Position = 5
Bottom = 4,
Position = 5
}
} }

View file

@ -1,22 +1,21 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
{
public enum EServerColName
{
def = 0,
configType,
remarks,
address,
port,
security,
network,
streamSecurity,
subRemarks,
delayVal,
speedVal,
todayDown, public enum EServerColName
todayUp, {
totalDown, def = 0,
totalUp configType,
} remarks,
address,
port,
security,
network,
streamSecurity,
subRemarks,
delayVal,
speedVal,
todayDown,
todayUp,
totalDown,
totalUp
} }

View file

@ -1,11 +1,10 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum ESpeedActionType
{ {
public enum ESpeedActionType Ping,
{ Tcping,
Ping, Realping,
Tcping, Speedtest,
Realping, Mixedtest
Speedtest,
Mixedtest
}
} }

View file

@ -1,10 +1,9 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum ESysProxyType
{ {
public enum ESysProxyType ForcedClear = 0,
{ ForcedChange = 1,
ForcedClear = 0, Unchanged = 2,
ForcedChange = 1, Pac = 3
Unchanged = 2,
Pac = 3
}
} }

View file

@ -1,8 +1,7 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public enum EViewAction
{ {
public enum EViewAction AdjustMainLvColWidth,
{ ProfilesFocus
AdjustMainLvColWidth,
ProfilesFocus
}
} }

View file

@ -1,68 +1,67 @@
using Newtonsoft.Json; using Newtonsoft.Json;
namespace v2rayN.Mode namespace v2rayN.Mode;
public class GitHubReleaseAsset
{ {
public class GitHubReleaseAsset [JsonProperty("url")] public string Url { get; set; }
{
[JsonProperty("url")] public string Url { get; set; }
[JsonProperty("id")] public int Id { get; set; } [JsonProperty("id")] public int Id { get; set; }
[JsonProperty("node_id")] public string NodeId { get; set; } [JsonProperty("node_id")] public string NodeId { get; set; }
[JsonProperty("name")] public string Name { get; set; } [JsonProperty("name")] public string Name { get; set; }
[JsonProperty("label")] public object Label { get; set; } [JsonProperty("label")] public object Label { get; set; }
[JsonProperty("content_type")] public string ContentType { get; set; } [JsonProperty("content_type")] public string ContentType { get; set; }
[JsonProperty("state")] public string State { get; set; } [JsonProperty("state")] public string State { get; set; }
[JsonProperty("size")] public int Size { get; set; } [JsonProperty("size")] public int Size { get; set; }
[JsonProperty("download_count")] public int DownloadCount { get; set; } [JsonProperty("download_count")] public int DownloadCount { get; set; }
[JsonProperty("created_at")] public DateTime CreatedAt { get; set; } [JsonProperty("created_at")] public DateTime CreatedAt { get; set; }
[JsonProperty("updated_at")] public DateTime UpdatedAt { get; set; } [JsonProperty("updated_at")] public DateTime UpdatedAt { get; set; }
[JsonProperty("browser_download_url")] public string BrowserDownloadUrl { get; set; } [JsonProperty("browser_download_url")] public string BrowserDownloadUrl { get; set; }
} }
public class GitHubRelease public class GitHubRelease
{ {
[JsonProperty("url")] public string Url { get; set; } [JsonProperty("url")] public string Url { get; set; }
[JsonProperty("assets_url")] public string AssetsUrl { get; set; } [JsonProperty("assets_url")] public string AssetsUrl { get; set; }
[JsonProperty("upload_url")] public string UploadUrl { get; set; } [JsonProperty("upload_url")] public string UploadUrl { get; set; }
[JsonProperty("html_url")] public string HtmlUrl { get; set; } [JsonProperty("html_url")] public string HtmlUrl { get; set; }
[JsonProperty("id")] public int Id { get; set; } [JsonProperty("id")] public int Id { get; set; }
[JsonProperty("node_id")] public string NodeId { get; set; } [JsonProperty("node_id")] public string NodeId { get; set; }
[JsonProperty("tag_name")] public string TagName { get; set; } [JsonProperty("tag_name")] public string TagName { get; set; }
[JsonProperty("target_commitish")] public string TargetCommitish { get; set; } [JsonProperty("target_commitish")] public string TargetCommitish { get; set; }
[JsonProperty("name")] public string Name { get; set; } [JsonProperty("name")] public string Name { get; set; }
[JsonProperty("draft")] public bool Draft { get; set; } [JsonProperty("draft")] public bool Draft { get; set; }
[JsonProperty("prerelease")] public bool Prerelease { get; set; } [JsonProperty("prerelease")] public bool Prerelease { get; set; }
[JsonProperty("created_at")] public DateTime CreatedAt { get; set; } [JsonProperty("created_at")] public DateTime CreatedAt { get; set; }
[JsonProperty("published_at")] public DateTime PublishedAt { get; set; } [JsonProperty("published_at")] public DateTime PublishedAt { get; set; }
[JsonProperty("assets")] public List<GitHubReleaseAsset> Assets { get; set; } [JsonProperty("assets")] public List<GitHubReleaseAsset> Assets { get; set; }
[JsonProperty("tarball_url")] public string TarballUrl { get; set; } [JsonProperty("tarball_url")] public string TarballUrl { get; set; }
[JsonProperty("zipball_url")] public string ZipballUrl { get; set; } [JsonProperty("zipball_url")] public string ZipballUrl { get; set; }
[JsonProperty("body")] public string Body { get; set; } [JsonProperty("body")] public string Body { get; set; }
}
} }

View file

@ -1,15 +1,14 @@
using SQLite; using SQLite;
namespace v2rayN.Mode namespace v2rayN.Mode;
{
[Serializable]
public class ProfileExItem
{
[PrimaryKey]
public string indexId { get; set; }
public int delay { get; set; } [Serializable]
public decimal speed { get; set; } public class ProfileExItem
public int sort { get; set; } {
} [PrimaryKey]
public string indexId { get; set; }
public int delay { get; set; }
public decimal speed { get; set; }
public int sort { get; set; }
} }

View file

@ -1,195 +1,194 @@
using SQLite; using SQLite;
using v2rayN.Base; using v2rayN.Base;
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class ProfileItem
{ {
[Serializable] public ProfileItem()
public class ProfileItem
{ {
public ProfileItem() indexId = string.Empty;
{ configType = EConfigType.VMess;
indexId = string.Empty; configVersion = 2;
configType = EConfigType.VMess; address = string.Empty;
configVersion = 2; port = 0;
address = string.Empty; id = string.Empty;
port = 0; alterId = 0;
id = string.Empty; security = string.Empty;
alterId = 0; network = string.Empty;
security = string.Empty; remarks = string.Empty;
network = string.Empty; headerType = string.Empty;
remarks = string.Empty; requestHost = string.Empty;
headerType = string.Empty; path = string.Empty;
requestHost = string.Empty; streamSecurity = string.Empty;
path = string.Empty; allowInsecure = string.Empty;
streamSecurity = string.Empty; subid = string.Empty;
allowInsecure = string.Empty; flow = string.Empty;
subid = string.Empty;
flow = string.Empty;
}
#region function
public string GetSummary()
{
string summary = string.Format("[{0}] ", (configType).ToString());
string[] arrAddr = address.Split('.');
string addr;
if (arrAddr.Length > 2)
{
addr = $"{arrAddr[0]}***{arrAddr[arrAddr.Length - 1]}";
}
else if (arrAddr.Length > 1)
{
addr = $"***{arrAddr[arrAddr.Length - 1]}";
}
else
{
addr = address;
}
switch (configType)
{
case EConfigType.VMess:
case EConfigType.Shadowsocks:
case EConfigType.Socks:
case EConfigType.VLESS:
case EConfigType.Trojan:
summary += string.Format("{0}({1}:{2})", remarks, addr, port);
break;
default:
summary += string.Format("{0}", remarks);
break;
}
return summary;
}
public List<string> GetAlpn()
{
if (Utils.IsNullOrEmpty(alpn))
{
return null;
}
else
{
return Utils.String2List(alpn);
}
}
public string GetNetwork()
{
if (Utils.IsNullOrEmpty(network) || !Global.networks.Contains(network))
{
return Global.DefaultNetwork;
}
return network.TrimEx();
}
#endregion function
[PrimaryKey]
public string indexId { get; set; }
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType { get; set; }
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion { get; set; }
/// <summary>
/// 远程服务器地址
/// </summary>
public string address { get; set; }
/// <summary>
/// 远程服务器端口
/// </summary>
public int port { get; set; }
/// <summary>
/// 远程服务器ID
/// </summary>
public string id { get; set; }
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId { get; set; }
/// <summary>
/// 本地安全策略
/// </summary>
public string security { get; set; }
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network { get; set; }
/// <summary>
///
/// </summary>
public string remarks { get; set; }
/// <summary>
/// 伪装类型
/// </summary>
public string headerType { get; set; }
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost { get; set; }
/// <summary>
/// ws h2 path
/// </summary>
public string path { get; set; }
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity { get; set; }
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure { get; set; }
/// <summary>
/// SubItem id
/// </summary>
public string subid { get; set; }
public bool isSub { get; set; } = true;
/// <summary>
/// VLESS flow
/// </summary>
public string flow { get; set; }
/// <summary>
/// tls sni
/// </summary>
public string sni { get; set; }
/// <summary>
/// tls alpn
/// </summary>
public string alpn { get; set; } = string.Empty;
public ECoreType? coreType { get; set; }
public int preSocksPort { get; set; }
public string fingerprint { get; set; }
public bool displayLog { get; set; } = true;
public string publicKey { get; set; }
public string shortId { get; set; }
public string spiderX { get; set; }
} }
#region function
public string GetSummary()
{
string summary = string.Format("[{0}] ", (configType).ToString());
string[] arrAddr = address.Split('.');
string addr;
if (arrAddr.Length > 2)
{
addr = $"{arrAddr[0]}***{arrAddr[arrAddr.Length - 1]}";
}
else if (arrAddr.Length > 1)
{
addr = $"***{arrAddr[arrAddr.Length - 1]}";
}
else
{
addr = address;
}
switch (configType)
{
case EConfigType.VMess:
case EConfigType.Shadowsocks:
case EConfigType.Socks:
case EConfigType.VLESS:
case EConfigType.Trojan:
summary += string.Format("{0}({1}:{2})", remarks, addr, port);
break;
default:
summary += string.Format("{0}", remarks);
break;
}
return summary;
}
public List<string> GetAlpn()
{
if (Utils.IsNullOrEmpty(alpn))
{
return null;
}
else
{
return Utils.String2List(alpn);
}
}
public string GetNetwork()
{
if (Utils.IsNullOrEmpty(network) || !Global.networks.Contains(network))
{
return Global.DefaultNetwork;
}
return network.TrimEx();
}
#endregion function
[PrimaryKey]
public string indexId { get; set; }
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType { get; set; }
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion { get; set; }
/// <summary>
/// 远程服务器地址
/// </summary>
public string address { get; set; }
/// <summary>
/// 远程服务器端口
/// </summary>
public int port { get; set; }
/// <summary>
/// 远程服务器ID
/// </summary>
public string id { get; set; }
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId { get; set; }
/// <summary>
/// 本地安全策略
/// </summary>
public string security { get; set; }
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network { get; set; }
/// <summary>
///
/// </summary>
public string remarks { get; set; }
/// <summary>
/// 伪装类型
/// </summary>
public string headerType { get; set; }
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost { get; set; }
/// <summary>
/// ws h2 path
/// </summary>
public string path { get; set; }
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity { get; set; }
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure { get; set; }
/// <summary>
/// SubItem id
/// </summary>
public string subid { get; set; }
public bool isSub { get; set; } = true;
/// <summary>
/// VLESS flow
/// </summary>
public string flow { get; set; }
/// <summary>
/// tls sni
/// </summary>
public string sni { get; set; }
/// <summary>
/// tls alpn
/// </summary>
public string alpn { get; set; } = string.Empty;
public ECoreType? coreType { get; set; }
public int preSocksPort { get; set; }
public string fingerprint { get; set; }
public bool displayLog { get; set; } = true;
public string publicKey { get; set; }
public string shortId { get; set; }
public string spiderX { get; set; }
} }

View file

@ -1,18 +1,17 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class ProfileItemModel : ProfileItem
{ {
[Serializable] public bool isActive { get; set; }
public class ProfileItemModel : ProfileItem public string subRemarks { get; set; }
{ public int delay { get; set; }
public bool isActive { get; set; } public decimal speed { get; set; }
public string subRemarks { get; set; } public int sort { get; set; }
public int delay { get; set; } public string delayVal { get; set; }
public decimal speed { get; set; } public string speedVal { get; set; }
public int sort { get; set; } public string todayUp { get; set; }
public string delayVal { get; set; } public string todayDown { get; set; }
public string speedVal { get; set; } public string totalUp { get; set; }
public string todayUp { get; set; } public string totalDown { get; set; }
public string todayDown { get; set; }
public string totalUp { get; set; }
public string totalDown { get; set; }
}
} }

View file

@ -1,22 +1,21 @@
using SQLite; using SQLite;
namespace v2rayN.Mode namespace v2rayN.Mode;
{
[Serializable]
public class RoutingItem
{
[PrimaryKey]
public string id { get; set; }
public string remarks { get; set; } [Serializable]
public string url { get; set; } public class RoutingItem
public string ruleSet { get; set; } {
public int ruleNum { get; set; } [PrimaryKey]
public bool enabled { get; set; } = true; public string id { get; set; }
public bool locked { get; set; }
public string customIcon { get; set; } public string remarks { get; set; }
public string domainStrategy { get; set; } public string url { get; set; }
public string domainStrategy4Singbox { get; set; } public string ruleSet { get; set; }
public int sort { get; set; } public int ruleNum { get; set; }
} public bool enabled { get; set; } = true;
public bool locked { get; set; }
public string customIcon { get; set; }
public string domainStrategy { get; set; }
public string domainStrategy4Singbox { get; set; }
public int sort { get; set; }
} }

View file

@ -1,8 +1,7 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class RoutingItemModel : RoutingItem
{ {
[Serializable] public bool isActive { get; set; }
public class RoutingItemModel : RoutingItem
{
public bool isActive { get; set; }
}
} }

View file

@ -1,25 +1,24 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class RulesItem
{ {
[Serializable] public string id { get; set; }
public class RulesItem public string type { get; set; }
{
public string id { get; set; }
public string type { get; set; }
public string port { get; set; } public string port { get; set; }
public List<string> inboundTag { get; set; } public List<string> inboundTag { get; set; }
public string outboundTag { get; set; } public string outboundTag { get; set; }
public List<string> ip { get; set; } public List<string> ip { get; set; }
public List<string> domain { get; set; } public List<string> domain { get; set; }
public List<string> protocol { get; set; } public List<string> protocol { get; set; }
public List<string> process { get; set; } public List<string> process { get; set; }
public bool enabled { get; set; } = true; public bool enabled { get; set; } = true;
}
} }

View file

@ -1,14 +1,13 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class RulesItemModel : RulesItem
{ {
[Serializable] public string inboundTags { get; set; }
public class RulesItemModel : RulesItem
{
public string inboundTags { get; set; }
public string ips { get; set; } public string ips { get; set; }
public string domains { get; set; } public string domains { get; set; }
public string protocols { get; set; } public string protocols { get; set; }
}
} }

View file

@ -1,40 +1,39 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
internal class ServerSpeedItem : ServerStatItem
{ {
[Serializable] public long proxyUp
internal class ServerSpeedItem : ServerStatItem
{ {
public long proxyUp get; set;
{
get; set;
}
public long proxyDown
{
get; set;
}
public long directUp
{
get; set;
}
public long directDown
{
get; set;
}
} }
[Serializable] public long proxyDown
public class TrafficItem
{ {
public ulong up get; set;
{ }
get; set;
}
public ulong down public long directUp
{ {
get; set; get; set;
} }
public long directDown
{
get; set;
}
}
[Serializable]
public class TrafficItem
{
public ulong up
{
get; set;
}
public ulong down
{
get; set;
} }
} }

View file

@ -1,39 +1,38 @@
using SQLite; using SQLite;
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class ServerStatItem
{ {
[Serializable] [PrimaryKey]
public class ServerStatItem public string indexId
{ {
[PrimaryKey] get; set;
public string indexId }
{
get; set;
}
public long totalUp public long totalUp
{ {
get; set; get; set;
} }
public long totalDown public long totalDown
{ {
get; set; get; set;
} }
public long todayUp public long todayUp
{ {
get; set; get; set;
} }
public long todayDown public long todayDown
{ {
get; set; get; set;
} }
public long dateNow public long dateNow
{ {
get; set; get; set;
}
} }
} }

View file

@ -1,13 +1,12 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
internal class ServerTestItem
{ {
[Serializable] public string indexId { get; set; }
internal class ServerTestItem public string address { get; set; }
{ public int port { get; set; }
public string indexId { get; set; } public EConfigType configType { get; set; }
public string address { get; set; } public bool allowTest { get; set; }
public int port { get; set; } public int delay { get; set; }
public EConfigType configType { get; set; }
public bool allowTest { get; set; }
public int delay { get; set; }
}
} }

View file

@ -1,212 +1,211 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
public class SingboxConfig
{ {
public class SingboxConfig public Log4Sbox log { get; set; }
{ public object dns { get; set; }
public Log4Sbox log { get; set; } public List<Inbound4Sbox> inbounds { get; set; }
public object dns { get; set; } public List<Outbound4Sbox> outbounds { get; set; }
public List<Inbound4Sbox> inbounds { get; set; } public Route4Sbox route { get; set; }
public List<Outbound4Sbox> outbounds { get; set; } public Experimental4Sbox experimental { get; set; }
public Route4Sbox route { get; set; } }
public Experimental4Sbox experimental { get; set; }
}
public class Log4Sbox public class Log4Sbox
{ {
public bool? disabled { get; set; } public bool? disabled { get; set; }
public string level { get; set; } public string level { get; set; }
public string output { get; set; } public string output { get; set; }
public bool timestamp { get; set; } public bool timestamp { get; set; }
} }
public class Dns4Sbox public class Dns4Sbox
{ {
public List<Server4Sbox> servers { get; set; } public List<Server4Sbox> servers { get; set; }
public List<Rule4Sbox> rules { get; set; } public List<Rule4Sbox> rules { get; set; }
public string? final { get; set; } public string? final { get; set; }
public string? strategy { get; set; } public string? strategy { get; set; }
public bool? disable_cache { get; set; } public bool? disable_cache { get; set; }
public bool? disable_expire { get; set; } public bool? disable_expire { get; set; }
public bool? independent_cache { get; set; } public bool? independent_cache { get; set; }
public bool? reverse_mapping { get; set; } public bool? reverse_mapping { get; set; }
public Fakeip4Sbox? fakeip { get; set; } public Fakeip4Sbox? fakeip { get; set; }
} }
public class Route4Sbox public class Route4Sbox
{ {
public bool? auto_detect_interface { get; set; } public bool? auto_detect_interface { get; set; }
public List<Rule4Sbox> rules { get; set; } public List<Rule4Sbox> rules { get; set; }
} }
[Serializable] [Serializable]
public class Rule4Sbox public class Rule4Sbox
{ {
public string outbound { get; set; } public string outbound { get; set; }
public string server { get; set; } public string server { get; set; }
public bool? disable_cache { get; set; } public bool? disable_cache { get; set; }
public List<string>? inbound { get; set; } public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; } public List<string>? protocol { get; set; }
public string type { get; set; } public string type { get; set; }
public string mode { get; set; } public string mode { get; set; }
public string network { get; set; } public string network { get; set; }
public List<int>? port { get; set; } public List<int>? port { get; set; }
public List<string>? port_range { get; set; } public List<string>? port_range { get; set; }
public List<string>? geosite { get; set; } public List<string>? geosite { get; set; }
public List<string>? domain { get; set; } public List<string>? domain { get; set; }
public List<string>? domain_suffix { get; set; } public List<string>? domain_suffix { get; set; }
public List<string>? domain_keyword { get; set; } public List<string>? domain_keyword { get; set; }
public List<string>? domain_regex { get; set; } public List<string>? domain_regex { get; set; }
public List<string>? geoip { get; set; } public List<string>? geoip { get; set; }
public List<string>? ip_cidr { get; set; } public List<string>? ip_cidr { get; set; }
public List<string>? source_ip_cidr { get; set; } public List<string>? source_ip_cidr { get; set; }
public List<string>? process_name { get; set; } public List<string>? process_name { get; set; }
} }
[Serializable] [Serializable]
public class Inbound4Sbox public class Inbound4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string listen { get; set; } public string listen { get; set; }
public int? listen_port { get; set; } public int? listen_port { get; set; }
public string? domain_strategy { get; set; } public string? domain_strategy { get; set; }
public string interface_name { get; set; } public string interface_name { get; set; }
public string inet4_address { get; set; } public string inet4_address { get; set; }
public string inet6_address { get; set; } public string inet6_address { get; set; }
public int? mtu { get; set; } public int? mtu { get; set; }
public bool? auto_route { get; set; } public bool? auto_route { get; set; }
public bool? strict_route { get; set; } public bool? strict_route { get; set; }
public bool? endpoint_independent_nat { get; set; } public bool? endpoint_independent_nat { get; set; }
public string? stack { get; set; } public string? stack { get; set; }
public bool? sniff { get; set; } public bool? sniff { get; set; }
public bool? sniff_override_destination { get; set; } public bool? sniff_override_destination { get; set; }
public List<User4Sbox> users { get; set; } public List<User4Sbox> users { get; set; }
} }
public class User4Sbox public class User4Sbox
{ {
public string username { get; set; } public string username { get; set; }
public string password { get; set; } public string password { get; set; }
} }
public class Outbound4Sbox public class Outbound4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string server { get; set; } public string server { get; set; }
public int? server_port { get; set; } public int? server_port { get; set; }
public string uuid { get; set; } public string uuid { get; set; }
public string security { get; set; } public string security { get; set; }
public int? alter_id { get; set; } public int? alter_id { get; set; }
public string flow { get; set; } public string flow { get; set; }
public int? up_mbps { get; set; } public int? up_mbps { get; set; }
public int? down_mbps { get; set; } public int? down_mbps { get; set; }
public string auth_str { get; set; } public string auth_str { get; set; }
public int? recv_window_conn { get; set; } public int? recv_window_conn { get; set; }
public int? recv_window { get; set; } public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; } public bool? disable_mtu_discovery { get; set; }
public string detour { get; set; } public string detour { get; set; }
public string method { get; set; } public string method { get; set; }
public string username { get; set; } public string username { get; set; }
public string password { get; set; } public string password { get; set; }
public string? version { get; set; } public string? version { get; set; }
public string? network { get; set; } public string? network { get; set; }
public string packet_encoding { get; set; } public string packet_encoding { get; set; }
public Tls4Sbox tls { get; set; } public Tls4Sbox tls { get; set; }
public Multiplex4Sbox multiplex { get; set; } public Multiplex4Sbox multiplex { get; set; }
public Transport4Sbox transport { get; set; } public Transport4Sbox transport { get; set; }
} }
public class Tls4Sbox public class Tls4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string server_name { get; set; } public string server_name { get; set; }
public bool? insecure { get; set; } public bool? insecure { get; set; }
public List<string> alpn { get; set; } public List<string> alpn { get; set; }
public Utls4Sbox utls { get; set; } public Utls4Sbox utls { get; set; }
public Reality4Sbox reality { get; set; } public Reality4Sbox reality { get; set; }
} }
public class Multiplex4Sbox public class Multiplex4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string protocol { get; set; } public string protocol { get; set; }
public int max_connections { get; set; } public int max_connections { get; set; }
public int min_streams { get; set; } public int min_streams { get; set; }
public int max_streams { get; set; } public int max_streams { get; set; }
public bool padding { get; set; } public bool padding { get; set; }
} }
public class Utls4Sbox public class Utls4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string fingerprint { get; set; } public string fingerprint { get; set; }
} }
public class Reality4Sbox public class Reality4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string public_key { get; set; } public string public_key { get; set; }
public string short_id { get; set; } public string short_id { get; set; }
} }
public class Transport4Sbox public class Transport4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public List<string>? host { get; set; } public List<string>? host { get; set; }
public string? path { get; set; } public string? path { get; set; }
public Headers4Sbox? headers { get; set; } public Headers4Sbox? headers { get; set; }
public string service_name { get; set; } public string service_name { get; set; }
public string idle_timeout { get; set; } public string idle_timeout { get; set; }
public string ping_timeout { get; set; } public string ping_timeout { get; set; }
public bool? permit_without_stream { get; set; } public bool? permit_without_stream { get; set; }
} }
public class Headers4Sbox public class Headers4Sbox
{ {
public string? Host { get; set; } public string? Host { get; set; }
} }
public class Server4Sbox public class Server4Sbox
{ {
public string tag { get; set; } public string tag { get; set; }
public string address { get; set; } public string address { get; set; }
public string address_resolver { get; set; } public string address_resolver { get; set; }
public string strategy { get; set; } public string strategy { get; set; }
public string detour { get; set; } public string detour { get; set; }
} }
public class Experimental4Sbox public class Experimental4Sbox
{ {
public V2ray_Api4Sbox v2ray_api { get; set; } public V2ray_Api4Sbox v2ray_api { get; set; }
public Clash_Api4Sbox clash_api { get; set; } public Clash_Api4Sbox clash_api { get; set; }
} }
public class V2ray_Api4Sbox public class V2ray_Api4Sbox
{ {
public string listen { get; set; } public string listen { get; set; }
public Stats4Sbox stats { get; set; } public Stats4Sbox stats { get; set; }
} }
public class Clash_Api4Sbox public class Clash_Api4Sbox
{ {
public string external_controller { get; set; } public string external_controller { get; set; }
public bool store_selected { get; set; } public bool store_selected { get; set; }
} }
public class Stats4Sbox public class Stats4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public List<string>? inbounds { get; set; } public List<string>? inbounds { get; set; }
public List<string>? outbounds { get; set; } public List<string>? outbounds { get; set; }
public List<string>? users { get; set; } public List<string>? users { get; set; }
} }
public class Fakeip4Sbox public class Fakeip4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string inet4_range { get; set; } public string inet4_range { get; set; }
public string inet6_range { get; set; } public string inet6_range { get; set; }
}
} }

View file

@ -1,18 +1,17 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
{
public class SsSIP008
{
public List<SsServer> servers { get; set; }
}
[Serializable] public class SsSIP008
public class SsServer {
{ public List<SsServer> servers { get; set; }
public string remarks { get; set; } }
public string server { get; set; }
public string server_port { get; set; } [Serializable]
public string method { get; set; } public class SsServer
public string password { get; set; } {
public string plugin { get; set; } public string remarks { get; set; }
} public string server { get; set; }
public string server_port { get; set; }
public string method { get; set; }
public string password { get; set; }
public string plugin { get; set; }
} }

View file

@ -1,31 +1,30 @@
using SQLite; using SQLite;
namespace v2rayN.Mode namespace v2rayN.Mode;
[Serializable]
public class SubItem
{ {
[Serializable] [PrimaryKey]
public class SubItem public string id { get; set; }
{
[PrimaryKey]
public string id { get; set; }
public string remarks { get; set; } public string remarks { get; set; }
public string url { get; set; } public string url { get; set; }
public string moreUrl { get; set; } public string moreUrl { get; set; }
public bool enabled { get; set; } = true; public bool enabled { get; set; } = true;
public string userAgent { get; set; } = string.Empty; public string userAgent { get; set; } = string.Empty;
public int sort { get; set; } public int sort { get; set; }
public string? filter { get; set; } public string? filter { get; set; }
public int autoUpdateInterval { get; set; } public int autoUpdateInterval { get; set; }
public long updateTime { get; set; } public long updateTime { get; set; }
public string? convertTarget { get; set; } public string? convertTarget { get; set; }
}
} }

View file

@ -1,20 +1,19 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
{
internal class SysproxyConfig
{
public bool UserSettingsRecorded;
public string Flags;
public string ProxyServer;
public string BypassList;
public string PacUrl;
public SysproxyConfig() internal class SysproxyConfig
{ {
UserSettingsRecorded = false; public bool UserSettingsRecorded;
Flags = "1"; public string Flags;
ProxyServer = ""; public string ProxyServer;
BypassList = ""; public string BypassList;
PacUrl = ""; public string PacUrl;
}
public SysproxyConfig()
{
UserSettingsRecorded = false;
Flags = "1";
ProxyServer = "";
BypassList = "";
PacUrl = "";
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,20 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
/// <summary>
/// Tcp伪装http的Request只要Host
/// </summary>
public class V2rayTcpRequest
{ {
/// <summary> /// <summary>
/// Tcp伪装http的Request只要Host ///
/// </summary> /// </summary>
public class V2rayTcpRequest public RequestHeaders headers { get; set; }
{ }
/// <summary>
///
/// </summary>
public RequestHeaders headers { get; set; }
}
public class RequestHeaders public class RequestHeaders
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public List<string> Host { get; set; } public List<string> Host { get; set; }
}
} }

View file

@ -1,84 +1,83 @@
namespace v2rayN.Mode namespace v2rayN.Mode;
/// <summary>
/// https://github.com/2dust/v2rayN/wiki/
/// </summary>
[Serializable]
internal class VmessQRCode
{ {
/// <summary> /// <summary>
/// https://github.com/2dust/v2rayN/wiki/ ///
/// </summary> /// </summary>
[Serializable] public string v { get; set; } = string.Empty;
internal class VmessQRCode
{
/// <summary>
///
/// </summary>
public string v { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string ps { get; set; } = string.Empty; public string ps { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string add { get; set; } = string.Empty; public string add { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string port { get; set; } = string.Empty; public string port { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string id { get; set; } = string.Empty; public string id { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string aid { get; set; } = string.Empty; public string aid { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string scy { get; set; } = string.Empty; public string scy { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string net { get; set; } = string.Empty; public string net { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string type { get; set; } = string.Empty; public string type { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string host { get; set; } = string.Empty; public string host { get; set; } = string.Empty;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string path { get; set; } = string.Empty; public string path { get; set; } = string.Empty;
/// <summary> /// <summary>
/// TLS /// TLS
/// </summary> /// </summary>
public string tls { get; set; } = string.Empty; public string tls { get; set; } = string.Empty;
/// <summary> /// <summary>
/// TLS SNI /// TLS SNI
/// </summary> /// </summary>
public string sni { get; set; } = string.Empty; public string sni { get; set; } = string.Empty;
/// <summary> /// <summary>
/// TLS alpn /// TLS alpn
/// </summary> /// </summary>
public string alpn { get; set; } = string.Empty; public string alpn { get; set; } = string.Empty;
/// <summary> /// <summary>
/// TLS fingerprint /// TLS fingerprint
/// </summary> /// </summary>
public string fp { get; set; } = string.Empty; public string fp { get; set; } = string.Empty;
}
} }

View file

@ -2,89 +2,88 @@
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace v2rayN.Tool namespace v2rayN.Tool;
public static class FileManager
{ {
public static class FileManager public static bool ByteArrayToFile(string fileName, byte[] content)
{ {
public static bool ByteArrayToFile(string fileName, byte[] content) try
{ {
try File.WriteAllBytes(fileName, content);
{ return true;
File.WriteAllBytes(fileName, content);
return true;
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
return false;
} }
catch (Exception ex)
public static void UncompressFile(string fileName, byte[] content)
{ {
try Utils.SaveLog(ex.Message, ex);
{
using FileStream fs = File.Create(fileName);
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
return false;
}
public static string NonExclusiveReadAllText(string path) public static void UncompressFile(string fileName, byte[] content)
{
try
{ {
return NonExclusiveReadAllText(path, Encoding.Default); using FileStream fs = File.Create(fileName);
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
} }
catch (Exception ex)
public static string NonExclusiveReadAllText(string path, Encoding encoding)
{ {
try Utils.SaveLog(ex.Message, ex);
{
using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs, encoding);
return sr.ReadToEnd();
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
throw;
}
} }
}
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName) public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
public static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{ {
try using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs, encoding);
return sr.ReadToEnd();
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
throw;
}
}
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
{
try
{
using ZipArchive archive = ZipFile.OpenRead(fileName);
foreach (ZipArchiveEntry entry in archive.Entries)
{ {
using ZipArchive archive = ZipFile.OpenRead(fileName); if (entry.Length == 0)
foreach (ZipArchiveEntry entry in archive.Entries)
{ {
if (entry.Length == 0) continue;
}
try
{
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
{ {
continue; continue;
} }
try entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
{ }
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName)) catch (IOException ex)
{ {
continue; Utils.SaveLog(ex.Message, ex);
}
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
}
catch (IOException ex)
{
Utils.SaveLog(ex.Message, ex);
}
} }
} }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return false;
}
return true;
} }
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
return false;
}
return true;
} }
} }

View file

@ -1,176 +1,175 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace v2rayN namespace v2rayN;
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
{ {
/* private IntPtr handle = IntPtr.Zero;
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable public Job()
{ {
private IntPtr handle = IntPtr.Zero; handle = CreateJobObject(IntPtr.Zero, null);
IntPtr extendedInfoPtr = IntPtr.Zero;
public Job() JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
{ {
handle = CreateJobObject(IntPtr.Zero, null); LimitFlags = 0x2000
IntPtr extendedInfoPtr = IntPtr.Zero; };
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
{
LimitFlags = 0x2000
};
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new() JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
{ {
BasicLimitInformation = info BasicLimitInformation = info
}; };
try try
{ {
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length); extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length)) (uint)length))
throw new Exception(string.Format("Unable to set information. Error: {0}", throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error())); Marshal.GetLastWin32Error()));
} }
finally finally
{
if (extendedInfoPtr != IntPtr.Zero)
{ {
if (extendedInfoPtr != IntPtr.Zero) Marshal.FreeHGlobal(extendedInfoPtr);
{
Marshal.FreeHGlobal(extendedInfoPtr);
}
} }
} }
}
public bool AddProcess(IntPtr processHandle) public bool AddProcess(IntPtr processHandle)
{
bool succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{ {
bool succ = AssignProcessToJobObject(handle, processHandle); Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
if (!succ)
{
Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
} }
public bool AddProcess(int processId) return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing)
{ {
return AddProcess(Process.GetProcessById(processId).Handle); // no managed objects to free
} }
#region IDisposable if (handle != IntPtr.Zero)
private bool disposed;
public void Dispose()
{ {
Dispose(true); CloseHandle(handle);
GC.SuppressFinalize(this); handle = IntPtr.Zero;
} }
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing)
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~Job()
{
Dispose(false);
}
#endregion IDisposable
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion Interop
} }
#region Helper classes ~Job()
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{ {
public UInt64 ReadOperationCount; Dispose(false);
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
} }
[StructLayout(LayoutKind.Sequential)] #endregion IDisposable
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)] #region Interop
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)] [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType [DllImport("kernel32.dll", SetLastError = true)]
{ private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion Helper classes [DllImport("kernel32.dll", SetLastError = true)]
} private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion Interop
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion Helper classes

View file

@ -3,53 +3,52 @@ using NLog.Config;
using NLog.Targets; using NLog.Targets;
using System.IO; using System.IO;
namespace v2rayN.Tool namespace v2rayN.Tool;
public class Logging
{ {
public class Logging public static void Setup()
{ {
public static void Setup() LoggingConfiguration config = new();
{ FileTarget fileTarget = new();
LoggingConfiguration config = new(); config.AddTarget("file", fileTarget);
FileTarget fileTarget = new(); fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
config.AddTarget("file", fileTarget); fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}"; config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt"); LogManager.Configuration = config;
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget)); }
LogManager.Configuration = config;
}
public static void LoggingEnabled(bool enable) public static void LoggingEnabled(bool enable)
{
if (!enable)
{ {
if (!enable) LogManager.SuspendLogging();
{
LogManager.SuspendLogging();
}
}
public static void ClearLogs()
{
Task.Run(() =>
{
try
{
var now = DateTime.Now.AddMonths(-1);
var dir = Utils.GetLogPath();
var files = Directory.GetFiles(dir, "*.txt");
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime < now)
{
try
{
file.Delete();
}
catch { }
}
}
}
catch { }
});
} }
} }
public static void ClearLogs()
{
Task.Run(() =>
{
try
{
var now = DateTime.Now.AddMonths(-1);
var dir = Utils.GetLogPath();
var files = Directory.GetFiles(dir, "*.txt");
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime < now)
{
try
{
file.Delete();
}
catch { }
}
}
}
catch { }
});
}
} }

View file

@ -1,50 +1,49 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace v2rayN.Tool namespace v2rayN.Tool;
public static class QueryableExtension
{ {
public static class QueryableExtension public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
{ {
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName) return _OrderBy<T>(query, propertyName, false);
{ }
return _OrderBy<T>(query, propertyName, false);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName) public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
{ {
return _OrderBy<T>(query, propertyName, true); return _OrderBy<T>(query, propertyName, true);
} }
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc) private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
{ {
string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal"; string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
var memberProp = typeof(T).GetProperty(propertyName); var memberProp = typeof(T).GetProperty(propertyName);
var method = typeof(QueryableExtension).GetMethod(methodname) var method = typeof(QueryableExtension).GetMethod(methodname)
.MakeGenericMethod(typeof(T), memberProp.PropertyType); .MakeGenericMethod(typeof(T), memberProp.PropertyType);
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp }); return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
} }
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty) public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public {//public
return query.OrderBy(_GetLamba<T, TProp>(memberProperty)); return query.OrderBy(_GetLamba<T, TProp>(memberProperty));
} }
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty) public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public {//public
return query.OrderByDescending(_GetLamba<T, TProp>(memberProperty)); return query.OrderByDescending(_GetLamba<T, TProp>(memberProperty));
} }
private static Expression<Func<T, TProp>> _GetLamba<T, TProp>(PropertyInfo memberProperty) private static Expression<Func<T, TProp>> _GetLamba<T, TProp>(PropertyInfo memberProperty)
{ {
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception(); if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
var thisArg = Expression.Parameter(typeof(T)); var thisArg = Expression.Parameter(typeof(T));
var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg); var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
return lamba; return lamba;
}
} }
} }

View file

@ -1,182 +1,181 @@
using v2rayN.Base; using v2rayN.Base;
namespace v2rayN.Tool namespace v2rayN.Tool;
public class SemanticVersion
{ {
public class SemanticVersion private int major;
private int minor;
private int patch;
private string version;
public SemanticVersion(int major, int minor, int patch)
{ {
private int major; this.major = major;
private int minor; this.minor = minor;
private int patch; this.patch = patch;
private string version; this.version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(int major, int minor, int patch) public SemanticVersion(string version)
{
this.version = version.RemovePrefix('v');
try
{ {
this.major = major; string[] parts = this.version.Split('.');
this.minor = minor; if (parts.Length == 2)
this.patch = patch;
this.version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string version)
{
this.version = version.RemovePrefix('v');
try
{ {
string[] parts = this.version.Split('.'); this.major = int.Parse(parts[0]);
if (parts.Length == 2) this.minor = int.Parse(parts[1]);
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = 0;
}
else if (parts.Length == 3)
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]);
}
else
{
throw new ArgumentException("Invalid version string");
}
}
catch
{
this.major = 0;
this.minor = 0;
this.patch = 0; this.patch = 0;
//this.version = "0.0.0";
} }
} else if (parts.Length == 3)
public override bool Equals(object? obj)
{
if (obj is SemanticVersion other)
{ {
return this.major == other.major && this.minor == other.minor && this.patch == other.patch; this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]);
} }
else else
{ {
return false; throw new ArgumentException("Invalid version string");
} }
} }
catch
public override int GetHashCode()
{ {
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode(); this.major = 0;
this.minor = 0;
this.patch = 0;
//this.version = "0.0.0";
} }
}
/// <summary> public override bool Equals(object? obj)
/// Use ToVersionString(string? prefix) instead if possible. {
/// </summary> if (obj is SemanticVersion other)
/// <returns>major.minor.patch</returns> {
public override string ToString() return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
}
/// <summary>
/// Use ToVersionString(string? prefix) instead if possible.
/// </summary>
/// <returns>major.minor.patch</returns>
public override string ToString()
{
return this.version;
}
public string ToVersionString(string? prefix = null)
{
if (prefix == null)
{ {
return this.version; return this.version;
} }
else
public string ToVersionString(string? prefix = null)
{ {
if (prefix == null) return $"{prefix}{this.version}";
{
return this.version;
}
else
{
return $"{prefix}{this.version}";
}
} }
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); }
#region Private
private bool GreaterEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return false;
}
else if (this.major > other.major)
{
return true;
}
else
{
if (this.minor < other.minor)
{
return false;
}
else if (this.minor > other.minor)
{
return true;
}
else
{
if (this.patch < other.patch)
{
return false;
}
else if (this.patch > other.patch)
{
return true;
}
else
{
return true;
}
}
}
}
private bool LessEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return true;
}
else if (this.major > other.major)
{
return false;
}
else
{
if (this.minor < other.minor)
{
return true;
}
else if (this.minor > other.minor)
{
return false;
}
else
{
if (this.patch < other.patch)
{
return true;
}
else if (this.patch > other.patch)
{
return false;
}
else
{
return true;
}
}
}
}
#endregion Private
} }
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); }
#region Private
private bool GreaterEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return false;
}
else if (this.major > other.major)
{
return true;
}
else
{
if (this.minor < other.minor)
{
return false;
}
else if (this.minor > other.minor)
{
return true;
}
else
{
if (this.patch < other.patch)
{
return false;
}
else if (this.patch > other.patch)
{
return true;
}
else
{
return true;
}
}
}
}
private bool LessEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return true;
}
else if (this.major > other.major)
{
return false;
}
else
{
if (this.minor < other.minor)
{
return true;
}
else if (this.minor > other.minor)
{
return false;
}
else
{
if (this.patch < other.patch)
{
return true;
}
else if (this.patch > other.patch)
{
return false;
}
else
{
return true;
}
}
}
}
#endregion Private
} }

View file

@ -1,24 +1,23 @@
using System.Windows; using System.Windows;
namespace v2rayN namespace v2rayN;
internal class UI
{ {
internal class UI private static readonly string caption = "v2rayN";
public static void Show(string msg)
{ {
private static readonly string caption = "v2rayN"; MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
}
public static void Show(string msg) public static void ShowWarning(string msg)
{ {
MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK); MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
} }
public static void ShowWarning(string msg) public static MessageBoxResult ShowYesNo(string msg)
{ {
MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK); return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
}
public static MessageBoxResult ShowYesNo(string msg)
{
return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -11,150 +11,149 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class AddServer2ViewModel : ReactiveValidationObject
{ {
public class AddServer2ViewModel : ReactiveValidationObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
[Reactive]
public ProfileItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> BrowseServerCmd { get; }
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
public bool IsModified { get; set; }
public AddServer2ViewModel(ProfileItem profileItem, Window view)
{ {
private static Config _config; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private NoticeHandler? _noticeHandler; _config = LazyConfig.Instance.GetConfig();
private Window _view;
[Reactive] if (profileItem.indexId.IsNullOrEmpty())
public ProfileItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> BrowseServerCmd { get; }
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
public bool IsModified { get; set; }
public AddServer2ViewModel(ProfileItem profileItem, Window view)
{ {
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); SelectedSource = profileItem;
_config = LazyConfig.Instance.GetConfig(); }
else
if (profileItem.indexId.IsNullOrEmpty()) {
{ SelectedSource = Utils.DeepCopy(profileItem);
SelectedSource = profileItem;
}
else
{
SelectedSource = Utils.DeepCopy(profileItem);
}
_view = view;
BrowseServerCmd = ReactiveCommand.Create(() =>
{
BrowseServer();
});
EditServerCmd = ReactiveCommand.Create(() =>
{
EditServer();
});
SaveServerCmd = ReactiveCommand.Create(() =>
{
SaveServer();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
private void SaveServer() _view = view;
BrowseServerCmd = ReactiveCommand.Create(() =>
{ {
string remarks = SelectedSource.remarks; BrowseServer();
if (Utils.IsNullOrEmpty(remarks)) });
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.address)) EditServerCmd = ReactiveCommand.Create(() =>
{ {
UI.Show(ResUI.FillServerAddressCustom); EditServer();
return; });
}
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); SaveServerCmd = ReactiveCommand.Create(() =>
if (item is null) {
{ SaveServer();
item = SelectedSource; });
}
else
{
item.remarks = SelectedSource.remarks;
item.address = SelectedSource.address;
item.coreType = SelectedSource.coreType;
item.displayLog = SelectedSource.displayLog;
item.preSocksPort = SelectedSource.preSocksPort;
}
if (ConfigHandler.EditCustomServer(ref _config, item) == 0) Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
{ }
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true; private void SaveServer()
} {
else string remarks = SelectedSource.remarks;
{ if (Utils.IsNullOrEmpty(remarks))
UI.Show(ResUI.OperationFailed); {
} UI.Show(ResUI.PleaseFillRemarks);
return;
} }
private void BrowseServer() if (Utils.IsNullOrEmpty(SelectedSource.address))
{ {
UI.Show(ResUI.CustomServerTips); UI.Show(ResUI.FillServerAddressCustom);
return;
OpenFileDialog fileDialog = new()
{
Multiselect = false,
Filter = "Config|*.json|YAML|*.yaml;*.yml|All|*.*"
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
item ??= SelectedSource;
item.address = fileName;
if (ConfigHandler.AddCustomServer(ref _config, item, false) == 0)
{
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer);
if (!Utils.IsNullOrEmpty(item.indexId))
{
SelectedSource = Utils.DeepCopy(item);
}
IsModified = true;
}
else
{
UI.ShowWarning(ResUI.FailedImportedCustomServer);
}
} }
private void EditServer() var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
if (item is null)
{ {
var address = SelectedSource.address; item = SelectedSource;
if (Utils.IsNullOrEmpty(address)) }
{ else
UI.Show(ResUI.FillServerAddressCustom); {
return; item.remarks = SelectedSource.remarks;
} item.address = SelectedSource.address;
item.coreType = SelectedSource.coreType;
item.displayLog = SelectedSource.displayLog;
item.preSocksPort = SelectedSource.preSocksPort;
}
address = Utils.GetConfigPath(address); if (ConfigHandler.EditCustomServer(ref _config, item) == 0)
if (File.Exists(address)) {
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.Show(ResUI.OperationFailed);
}
}
private void BrowseServer()
{
UI.Show(ResUI.CustomServerTips);
OpenFileDialog fileDialog = new()
{
Multiselect = false,
Filter = "Config|*.json|YAML|*.yaml;*.yml|All|*.*"
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
item ??= SelectedSource;
item.address = fileName;
if (ConfigHandler.AddCustomServer(ref _config, item, false) == 0)
{
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer);
if (!Utils.IsNullOrEmpty(item.indexId))
{ {
Utils.ProcessStart(address); SelectedSource = Utils.DeepCopy(item);
}
else
{
_noticeHandler?.Enqueue(ResUI.FailedReadConfiguration);
} }
IsModified = true;
}
else
{
UI.ShowWarning(ResUI.FailedImportedCustomServer);
}
}
private void EditServer()
{
var address = SelectedSource.address;
if (Utils.IsNullOrEmpty(address))
{
UI.Show(ResUI.FillServerAddressCustom);
return;
}
address = Utils.GetConfigPath(address);
if (File.Exists(address))
{
Utils.ProcessStart(address);
}
else
{
_noticeHandler?.Enqueue(ResUI.FailedReadConfiguration);
} }
} }
} }

View file

@ -8,155 +8,154 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class AddServerViewModel : ReactiveObject
{ {
public class AddServerViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
[Reactive]
public ProfileItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public AddServerViewModel(ProfileItem profileItem, Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
[Reactive] if (profileItem.id.IsNullOrEmpty())
public ProfileItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public AddServerViewModel(ProfileItem profileItem, Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); profileItem.network = Global.DefaultNetwork;
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); profileItem.headerType = Global.None;
_view = view; profileItem.requestHost = "";
profileItem.streamSecurity = "";
if (profileItem.id.IsNullOrEmpty()) SelectedSource = profileItem;
{ }
profileItem.network = Global.DefaultNetwork; else
profileItem.headerType = Global.None; {
profileItem.requestHost = ""; SelectedSource = Utils.DeepCopy(profileItem);
profileItem.streamSecurity = "";
SelectedSource = profileItem;
}
else
{
SelectedSource = Utils.DeepCopy(profileItem);
}
SaveCmd = ReactiveCommand.Create(() =>
{
SaveServer();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
private void SaveServer() SaveCmd = ReactiveCommand.Create(() =>
{ {
if (Utils.IsNullOrEmpty(SelectedSource.remarks)) SaveServer();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
private void SaveServer()
{
if (Utils.IsNullOrEmpty(SelectedSource.remarks))
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.address))
{
UI.Show(ResUI.FillServerAddress);
return;
}
var port = SelectedSource.port.ToString();
if (Utils.IsNullOrEmpty(port) || !Utils.IsNumberic(port)
|| SelectedSource.port <= 0 || SelectedSource.port >= Global.MaxPort)
{
UI.Show(ResUI.FillCorrectServerPort);
return;
}
if (SelectedSource.configType == EConfigType.Shadowsocks)
{
if (Utils.IsNullOrEmpty(SelectedSource.id))
{ {
UI.Show(ResUI.PleaseFillRemarks); UI.Show(ResUI.FillPassword);
return; return;
} }
if (Utils.IsNullOrEmpty(SelectedSource.security))
if (Utils.IsNullOrEmpty(SelectedSource.address))
{ {
UI.Show(ResUI.FillServerAddress); UI.Show(ResUI.PleaseSelectEncryption);
return; return;
} }
var port = SelectedSource.port.ToString(); }
if (Utils.IsNullOrEmpty(port) || !Utils.IsNumberic(port) if (SelectedSource.configType != EConfigType.Socks)
|| SelectedSource.port <= 0 || SelectedSource.port >= Global.MaxPort) {
if (Utils.IsNullOrEmpty(SelectedSource.id))
{ {
UI.Show(ResUI.FillCorrectServerPort); UI.Show(ResUI.FillUUID);
return; return;
} }
if (SelectedSource.configType == EConfigType.Shadowsocks) }
{
if (Utils.IsNullOrEmpty(SelectedSource.id))
{
UI.Show(ResUI.FillPassword);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.security))
{
UI.Show(ResUI.PleaseSelectEncryption);
return;
}
}
if (SelectedSource.configType != EConfigType.Socks)
{
if (Utils.IsNullOrEmpty(SelectedSource.id))
{
UI.Show(ResUI.FillUUID);
return;
}
}
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
if (item is null) if (item is null)
{ {
item = SelectedSource; item = SelectedSource;
} }
else else
{ {
item.coreType = SelectedSource.coreType; item.coreType = SelectedSource.coreType;
item.remarks = SelectedSource.remarks; item.remarks = SelectedSource.remarks;
item.address = SelectedSource.address; item.address = SelectedSource.address;
item.port = SelectedSource.port; item.port = SelectedSource.port;
item.id = SelectedSource.id; item.id = SelectedSource.id;
item.alterId = SelectedSource.alterId; item.alterId = SelectedSource.alterId;
item.security = SelectedSource.security; item.security = SelectedSource.security;
item.flow = SelectedSource.flow; item.flow = SelectedSource.flow;
item.network = SelectedSource.network; item.network = SelectedSource.network;
item.headerType = SelectedSource.headerType; item.headerType = SelectedSource.headerType;
item.requestHost = SelectedSource.requestHost; item.requestHost = SelectedSource.requestHost;
item.path = SelectedSource.path; item.path = SelectedSource.path;
item.streamSecurity = SelectedSource.streamSecurity; item.streamSecurity = SelectedSource.streamSecurity;
item.sni = SelectedSource.sni; item.sni = SelectedSource.sni;
item.allowInsecure = SelectedSource.allowInsecure; item.allowInsecure = SelectedSource.allowInsecure;
item.fingerprint = SelectedSource.fingerprint; item.fingerprint = SelectedSource.fingerprint;
item.alpn = SelectedSource.alpn; item.alpn = SelectedSource.alpn;
item.publicKey = SelectedSource.publicKey; item.publicKey = SelectedSource.publicKey;
item.shortId = SelectedSource.shortId; item.shortId = SelectedSource.shortId;
item.spiderX = SelectedSource.spiderX; item.spiderX = SelectedSource.spiderX;
} }
int ret = -1; int ret = -1;
switch (item.configType) switch (item.configType)
{ {
case EConfigType.VMess: case EConfigType.VMess:
ret = ConfigHandler.AddServer(ref _config, item); ret = ConfigHandler.AddServer(ref _config, item);
break; break;
case EConfigType.Shadowsocks: case EConfigType.Shadowsocks:
ret = ConfigHandler.AddShadowsocksServer(ref _config, item); ret = ConfigHandler.AddShadowsocksServer(ref _config, item);
break; break;
case EConfigType.Socks: case EConfigType.Socks:
ret = ConfigHandler.AddSocksServer(ref _config, item); ret = ConfigHandler.AddSocksServer(ref _config, item);
break; break;
case EConfigType.VLESS: case EConfigType.VLESS:
ret = ConfigHandler.AddVlessServer(ref _config, item); ret = ConfigHandler.AddVlessServer(ref _config, item);
break; break;
case EConfigType.Trojan: case EConfigType.Trojan:
ret = ConfigHandler.AddTrojanServer(ref _config, item); ret = ConfigHandler.AddTrojanServer(ref _config, item);
break; break;
} }
if (ret == 0) if (ret == 0)
{ {
_noticeHandler?.Enqueue(ResUI.OperationSuccess); _noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true; _view.DialogResult = true;
//_view?.Close(); //_view?.Close();
} }
else else
{ {
UI.Show(ResUI.OperationFailed); UI.Show(ResUI.OperationFailed);
}
} }
} }
} }

View file

@ -7,104 +7,103 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class DNSSettingViewModel : ReactiveObject
{ {
public class DNSSettingViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
[Reactive] public string domainStrategy4Freedom { get; set; }
[Reactive] public string normalDNS { get; set; }
[Reactive] public string normalDNS2 { get; set; }
[Reactive] public string tunDNS2 { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
public DNSSettingViewModel(Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
[Reactive] public string domainStrategy4Freedom { get; set; } var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray);
[Reactive] public string normalDNS { get; set; } domainStrategy4Freedom = item?.domainStrategy4Freedom!;
[Reactive] public string normalDNS2 { get; set; } normalDNS = item?.normalDNS!;
[Reactive] public string tunDNS2 { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; } var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; } normalDNS2 = item2?.normalDNS!;
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; } tunDNS2 = item2?.tunDNS!;
public DNSSettingViewModel(Window view) SaveCmd = ReactiveCommand.Create(() =>
{ {
_config = LazyConfig.Instance.GetConfig(); SaveSetting();
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); });
_view = view;
var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); ImportDefConfig4V2rayCmd = ReactiveCommand.Create(() =>
domainStrategy4Freedom = item?.domainStrategy4Freedom!;
normalDNS = item?.normalDNS!;
var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
normalDNS2 = item2?.normalDNS!;
tunDNS2 = item2?.tunDNS!;
SaveCmd = ReactiveCommand.Create(() =>
{
SaveSetting();
});
ImportDefConfig4V2rayCmd = ReactiveCommand.Create(() =>
{
normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName);
});
ImportDefConfig4SingboxCmd = ReactiveCommand.Create(() =>
{
normalDNS2 = Utils.GetEmbedText(Global.DNSSingboxNormalFileName);
tunDNS2 = Utils.GetEmbedText(Global.TunSingboxDNSFileName);
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
private void SaveSetting()
{ {
if (!Utils.IsNullOrEmpty(normalDNS)) normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName);
});
ImportDefConfig4SingboxCmd = ReactiveCommand.Create(() =>
{
normalDNS2 = Utils.GetEmbedText(Global.DNSSingboxNormalFileName);
tunDNS2 = Utils.GetEmbedText(Global.TunSingboxDNSFileName);
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
private void SaveSetting()
{
if (!Utils.IsNullOrEmpty(normalDNS))
{
var obj = Utils.ParseJson(normalDNS);
if (obj != null && obj.ContainsKey("servers") == true)
{ {
var obj = Utils.ParseJson(normalDNS);
if (obj != null && obj.ContainsKey("servers") == true)
{
}
else
{
if (normalDNS.Contains("{") || normalDNS.Contains("}"))
{
UI.Show(ResUI.FillCorrectDNSText);
return;
}
}
} }
if (!Utils.IsNullOrEmpty(normalDNS2)) else
{ {
var obj2 = Utils.FromJson<Dns4Sbox>(normalDNS2); if (normalDNS.Contains("{") || normalDNS.Contains("}"))
if (obj2 == null)
{ {
UI.Show(ResUI.FillCorrectDNSText); UI.Show(ResUI.FillCorrectDNSText);
return; return;
} }
} }
if (!Utils.IsNullOrEmpty(tunDNS2))
{
var obj2 = Utils.FromJson<Dns4Sbox>(tunDNS2);
if (obj2 == null)
{
UI.Show(ResUI.FillCorrectDNSText);
return;
}
}
var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray);
item.domainStrategy4Freedom = domainStrategy4Freedom;
item.normalDNS = normalDNS;
ConfigHandler.SaveDNSItems(_config, item);
var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
item2.normalDNS = Utils.ToJson(Utils.ParseJson(normalDNS2));
item2.tunDNS = Utils.ToJson(Utils.ParseJson(tunDNS2));
ConfigHandler.SaveDNSItems(_config, item2);
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
} }
if (!Utils.IsNullOrEmpty(normalDNS2))
{
var obj2 = Utils.FromJson<Dns4Sbox>(normalDNS2);
if (obj2 == null)
{
UI.Show(ResUI.FillCorrectDNSText);
return;
}
}
if (!Utils.IsNullOrEmpty(tunDNS2))
{
var obj2 = Utils.FromJson<Dns4Sbox>(tunDNS2);
if (obj2 == null)
{
UI.Show(ResUI.FillCorrectDNSText);
return;
}
}
var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray);
item.domainStrategy4Freedom = domainStrategy4Freedom;
item.normalDNS = normalDNS;
ConfigHandler.SaveDNSItems(_config, item);
var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
item2.normalDNS = Utils.ToJson(Utils.ParseJson(normalDNS2));
item2.tunDNS = Utils.ToJson(Utils.ParseJson(tunDNS2));
ConfigHandler.SaveDNSItems(_config, item2);
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -7,362 +7,361 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class OptionSettingViewModel : ReactiveObject
{ {
public class OptionSettingViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
#region Core
[Reactive] public int localPort { get; set; }
[Reactive] public bool udpEnabled { get; set; }
[Reactive] public bool sniffingEnabled { get; set; }
[Reactive] public bool routeOnly { get; set; }
[Reactive] public bool allowLANConn { get; set; }
[Reactive] public bool newPort4LAN { get; set; }
[Reactive] public string user { get; set; }
[Reactive] public string pass { get; set; }
[Reactive] public bool muxEnabled { get; set; }
[Reactive] public bool logEnabled { get; set; }
[Reactive] public string loglevel { get; set; }
[Reactive] public bool defAllowInsecure { get; set; }
[Reactive] public string defFingerprint { get; set; }
[Reactive] public string defUserAgent { get; set; }
[Reactive] public string mux4SboxProtocol { get; set; }
#endregion Core
#region Core KCP
//[Reactive] public int Kcpmtu { get; set; }
//[Reactive] public int Kcptti { get; set; }
//[Reactive] public int KcpuplinkCapacity { get; set; }
//[Reactive] public int KcpdownlinkCapacity { get; set; }
//[Reactive] public int KcpreadBufferSize { get; set; }
//[Reactive] public int KcpwriteBufferSize { get; set; }
//[Reactive] public bool Kcpcongestion { get; set; }
#endregion Core KCP
#region UI
[Reactive] public bool AutoRun { get; set; }
[Reactive] public bool EnableStatistics { get; set; }
[Reactive] public bool KeepOlderDedupl { get; set; }
[Reactive] public bool IgnoreGeoUpdateCore { get; set; }
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; }
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
[Reactive] public bool AutoHideStartup { get; set; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
[Reactive] public bool EnableDragDropSort { get; set; }
[Reactive] public bool DoubleClick2Activate { get; set; }
[Reactive] public int autoUpdateInterval { get; set; }
[Reactive] public int trayMenuServersLimit { get; set; }
[Reactive] public string currentFontFamily { get; set; }
[Reactive] public int SpeedTestTimeout { get; set; }
[Reactive] public string SpeedTestUrl { get; set; }
[Reactive] public bool EnableHWA { get; set; }
[Reactive] public string SubConvertUrl { get; set; }
#endregion UI
#region System proxy
[Reactive] public string systemProxyAdvancedProtocol { get; set; }
[Reactive] public string systemProxyExceptions { get; set; }
#endregion System proxy
#region Tun mode
[Reactive] public bool TunStrictRoute { get; set; }
[Reactive] public string TunStack { get; set; }
[Reactive] public int TunMtu { get; set; }
#endregion Tun mode
#region CoreType
[Reactive] public string CoreType1 { get; set; }
[Reactive] public string CoreType2 { get; set; }
[Reactive] public string CoreType3 { get; set; }
[Reactive] public string CoreType4 { get; set; }
[Reactive] public string CoreType5 { get; set; }
[Reactive] public string CoreType6 { get; set; }
#endregion CoreType
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public OptionSettingViewModel(Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
#region Core #region Core
[Reactive] public int localPort { get; set; } var inbound = _config.inbound[0];
[Reactive] public bool udpEnabled { get; set; } localPort = inbound.localPort;
[Reactive] public bool sniffingEnabled { get; set; } udpEnabled = inbound.udpEnabled;
[Reactive] public bool routeOnly { get; set; } sniffingEnabled = inbound.sniffingEnabled;
[Reactive] public bool allowLANConn { get; set; } routeOnly = inbound.routeOnly;
[Reactive] public bool newPort4LAN { get; set; } allowLANConn = inbound.allowLANConn;
[Reactive] public string user { get; set; } newPort4LAN = inbound.newPort4LAN;
[Reactive] public string pass { get; set; } user = inbound.user;
[Reactive] public bool muxEnabled { get; set; } pass = inbound.pass;
[Reactive] public bool logEnabled { get; set; } muxEnabled = _config.coreBasicItem.muxEnabled;
[Reactive] public string loglevel { get; set; } logEnabled = _config.coreBasicItem.logEnabled;
[Reactive] public bool defAllowInsecure { get; set; } loglevel = _config.coreBasicItem.loglevel;
[Reactive] public string defFingerprint { get; set; } defAllowInsecure = _config.coreBasicItem.defAllowInsecure;
[Reactive] public string defUserAgent { get; set; } defFingerprint = _config.coreBasicItem.defFingerprint;
[Reactive] public string mux4SboxProtocol { get; set; } defUserAgent = _config.coreBasicItem.defUserAgent;
mux4SboxProtocol = _config.mux4Sbox.protocol;
#endregion Core #endregion Core
#region Core KCP #region Core KCP
//[Reactive] public int Kcpmtu { get; set; } //Kcpmtu = _config.kcpItem.mtu;
//[Reactive] public int Kcptti { get; set; } //Kcptti = _config.kcpItem.tti;
//[Reactive] public int KcpuplinkCapacity { get; set; } //KcpuplinkCapacity = _config.kcpItem.uplinkCapacity;
//[Reactive] public int KcpdownlinkCapacity { get; set; } //KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity;
//[Reactive] public int KcpreadBufferSize { get; set; } //KcpreadBufferSize = _config.kcpItem.readBufferSize;
//[Reactive] public int KcpwriteBufferSize { get; set; } //KcpwriteBufferSize = _config.kcpItem.writeBufferSize;
//[Reactive] public bool Kcpcongestion { get; set; } //Kcpcongestion = _config.kcpItem.congestion;
#endregion Core KCP #endregion Core KCP
#region UI #region UI
[Reactive] public bool AutoRun { get; set; } AutoRun = _config.guiItem.autoRun;
[Reactive] public bool EnableStatistics { get; set; } EnableStatistics = _config.guiItem.enableStatistics;
[Reactive] public bool KeepOlderDedupl { get; set; } KeepOlderDedupl = _config.guiItem.keepOlderDedupl;
[Reactive] public bool IgnoreGeoUpdateCore { get; set; } IgnoreGeoUpdateCore = _config.guiItem.ignoreGeoUpdateCore;
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; } EnableAutoAdjustMainLvColWidth = _config.uiItem.enableAutoAdjustMainLvColWidth;
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; } EnableSecurityProtocolTls13 = _config.guiItem.enableSecurityProtocolTls13;
[Reactive] public bool AutoHideStartup { get; set; } AutoHideStartup = _config.uiItem.autoHideStartup;
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate;
[Reactive] public bool EnableDragDropSort { get; set; } EnableDragDropSort = _config.uiItem.enableDragDropSort;
[Reactive] public bool DoubleClick2Activate { get; set; } DoubleClick2Activate = _config.uiItem.doubleClick2Activate;
[Reactive] public int autoUpdateInterval { get; set; } autoUpdateInterval = _config.guiItem.autoUpdateInterval;
[Reactive] public int trayMenuServersLimit { get; set; } trayMenuServersLimit = _config.guiItem.trayMenuServersLimit;
[Reactive] public string currentFontFamily { get; set; } currentFontFamily = _config.uiItem.currentFontFamily;
[Reactive] public int SpeedTestTimeout { get; set; } SpeedTestTimeout = _config.speedTestItem.speedTestTimeout;
[Reactive] public string SpeedTestUrl { get; set; } SpeedTestUrl = _config.speedTestItem.speedTestUrl;
[Reactive] public bool EnableHWA { get; set; } EnableHWA = _config.guiItem.enableHWA;
[Reactive] public string SubConvertUrl { get; set; } SubConvertUrl = _config.constItem.subConvertUrl;
#endregion UI #endregion UI
#region System proxy #region System proxy
[Reactive] public string systemProxyAdvancedProtocol { get; set; } systemProxyAdvancedProtocol = _config.systemProxyAdvancedProtocol;
[Reactive] public string systemProxyExceptions { get; set; } systemProxyExceptions = _config.systemProxyExceptions;
#endregion System proxy #endregion System proxy
#region Tun mode #region Tun mode
[Reactive] public bool TunStrictRoute { get; set; } TunStrictRoute = _config.tunModeItem.strictRoute;
[Reactive] public string TunStack { get; set; } TunStack = _config.tunModeItem.stack;
[Reactive] public int TunMtu { get; set; } TunMtu = _config.tunModeItem.mtu;
#endregion Tun mode #endregion Tun mode
#region CoreType InitCoreType();
[Reactive] public string CoreType1 { get; set; } SaveCmd = ReactiveCommand.Create(() =>
[Reactive] public string CoreType2 { get; set; }
[Reactive] public string CoreType3 { get; set; }
[Reactive] public string CoreType4 { get; set; }
[Reactive] public string CoreType5 { get; set; }
[Reactive] public string CoreType6 { get; set; }
#endregion CoreType
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public OptionSettingViewModel(Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); SaveSetting();
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); });
_view = view;
#region Core Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
var inbound = _config.inbound[0]; private void InitCoreType()
localPort = inbound.localPort; {
udpEnabled = inbound.udpEnabled; if (_config.coreTypeItem == null)
sniffingEnabled = inbound.sniffingEnabled; {
routeOnly = inbound.routeOnly; _config.coreTypeItem = new List<CoreTypeItem>();
allowLANConn = inbound.allowLANConn;
newPort4LAN = inbound.newPort4LAN;
user = inbound.user;
pass = inbound.pass;
muxEnabled = _config.coreBasicItem.muxEnabled;
logEnabled = _config.coreBasicItem.logEnabled;
loglevel = _config.coreBasicItem.loglevel;
defAllowInsecure = _config.coreBasicItem.defAllowInsecure;
defFingerprint = _config.coreBasicItem.defFingerprint;
defUserAgent = _config.coreBasicItem.defUserAgent;
mux4SboxProtocol = _config.mux4Sbox.protocol;
#endregion Core
#region Core KCP
//Kcpmtu = _config.kcpItem.mtu;
//Kcptti = _config.kcpItem.tti;
//KcpuplinkCapacity = _config.kcpItem.uplinkCapacity;
//KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity;
//KcpreadBufferSize = _config.kcpItem.readBufferSize;
//KcpwriteBufferSize = _config.kcpItem.writeBufferSize;
//Kcpcongestion = _config.kcpItem.congestion;
#endregion Core KCP
#region UI
AutoRun = _config.guiItem.autoRun;
EnableStatistics = _config.guiItem.enableStatistics;
KeepOlderDedupl = _config.guiItem.keepOlderDedupl;
IgnoreGeoUpdateCore = _config.guiItem.ignoreGeoUpdateCore;
EnableAutoAdjustMainLvColWidth = _config.uiItem.enableAutoAdjustMainLvColWidth;
EnableSecurityProtocolTls13 = _config.guiItem.enableSecurityProtocolTls13;
AutoHideStartup = _config.uiItem.autoHideStartup;
EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate;
EnableDragDropSort = _config.uiItem.enableDragDropSort;
DoubleClick2Activate = _config.uiItem.doubleClick2Activate;
autoUpdateInterval = _config.guiItem.autoUpdateInterval;
trayMenuServersLimit = _config.guiItem.trayMenuServersLimit;
currentFontFamily = _config.uiItem.currentFontFamily;
SpeedTestTimeout = _config.speedTestItem.speedTestTimeout;
SpeedTestUrl = _config.speedTestItem.speedTestUrl;
EnableHWA = _config.guiItem.enableHWA;
SubConvertUrl = _config.constItem.subConvertUrl;
#endregion UI
#region System proxy
systemProxyAdvancedProtocol = _config.systemProxyAdvancedProtocol;
systemProxyExceptions = _config.systemProxyExceptions;
#endregion System proxy
#region Tun mode
TunStrictRoute = _config.tunModeItem.strictRoute;
TunStack = _config.tunModeItem.stack;
TunMtu = _config.tunModeItem.mtu;
#endregion Tun mode
InitCoreType();
SaveCmd = ReactiveCommand.Create(() =>
{
SaveSetting();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
private void InitCoreType() foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
{ {
if (_config.coreTypeItem == null) if (_config.coreTypeItem.FindIndex(t => t.configType == it) >= 0)
{ {
_config.coreTypeItem = new List<CoreTypeItem>(); continue;
} }
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) _config.coreTypeItem.Add(new CoreTypeItem()
{ {
if (_config.coreTypeItem.FindIndex(t => t.configType == it) >= 0) configType = it,
{ coreType = ECoreType.Xray
continue;
}
_config.coreTypeItem.Add(new CoreTypeItem()
{
configType = it,
coreType = ECoreType.Xray
});
}
_config.coreTypeItem.ForEach(it =>
{
var type = it.coreType.ToString();
switch ((int)it.configType)
{
case 1:
CoreType1 = type;
break;
case 2:
CoreType2 = type;
break;
case 3:
CoreType3 = type;
break;
case 4:
CoreType4 = type;
break;
case 5:
CoreType5 = type;
break;
case 6:
CoreType6 = type;
break;
}
}); });
} }
_config.coreTypeItem.ForEach(it =>
private void SaveSetting()
{ {
if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString()) var type = it.coreType.ToString();
|| localPort <= 0 || localPort >= Global.MaxPort) switch ((int)it.configType)
{ {
UI.Show(ResUI.FillLocalListeningPort); case 1:
return; CoreType1 = type;
break;
case 2:
CoreType2 = type;
break;
case 3:
CoreType3 = type;
break;
case 4:
CoreType4 = type;
break;
case 5:
CoreType5 = type;
break;
case 6:
CoreType6 = type;
break;
} }
});
}
//if (Utils.IsNullOrEmpty(Kcpmtu.ToString()) || !Utils.IsNumberic(Kcpmtu.ToString()) private void SaveSetting()
// || Utils.IsNullOrEmpty(Kcptti.ToString()) || !Utils.IsNumberic(Kcptti.ToString()) {
// || Utils.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utils.IsNumberic(KcpuplinkCapacity.ToString()) if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString())
// || Utils.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utils.IsNumberic(KcpdownlinkCapacity.ToString()) || localPort <= 0 || localPort >= Global.MaxPort)
// || Utils.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utils.IsNumberic(KcpreadBufferSize.ToString()) {
// || Utils.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utils.IsNumberic(KcpwriteBufferSize.ToString())) UI.Show(ResUI.FillLocalListeningPort);
//{ return;
// UI.Show(ResUI.FillKcpParameters);
// return;
//}
//Core
_config.inbound[0].localPort = localPort;
_config.inbound[0].udpEnabled = udpEnabled;
_config.inbound[0].sniffingEnabled = sniffingEnabled;
_config.inbound[0].routeOnly = routeOnly;
_config.inbound[0].allowLANConn = allowLANConn;
_config.inbound[0].newPort4LAN = newPort4LAN;
_config.inbound[0].user = user;
_config.inbound[0].pass = pass;
if (_config.inbound.Count > 1)
{
_config.inbound.RemoveAt(1);
}
_config.coreBasicItem.logEnabled = logEnabled;
_config.coreBasicItem.loglevel = loglevel;
_config.coreBasicItem.muxEnabled = muxEnabled;
_config.coreBasicItem.defAllowInsecure = defAllowInsecure;
_config.coreBasicItem.defFingerprint = defFingerprint;
_config.coreBasicItem.defUserAgent = defUserAgent;
_config.mux4Sbox.protocol = mux4SboxProtocol;
//Kcp
//_config.kcpItem.mtu = Kcpmtu;
//_config.kcpItem.tti = Kcptti;
//_config.kcpItem.uplinkCapacity = KcpuplinkCapacity;
//_config.kcpItem.downlinkCapacity = KcpdownlinkCapacity;
//_config.kcpItem.readBufferSize = KcpreadBufferSize;
//_config.kcpItem.writeBufferSize = KcpwriteBufferSize;
//_config.kcpItem.congestion = Kcpcongestion;
//UI
Utils.SetAutoRun(AutoRun);
_config.guiItem.autoRun = AutoRun;
_config.guiItem.enableStatistics = EnableStatistics;
_config.guiItem.keepOlderDedupl = KeepOlderDedupl;
_config.guiItem.ignoreGeoUpdateCore = IgnoreGeoUpdateCore;
_config.uiItem.enableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth;
_config.guiItem.enableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
_config.uiItem.autoHideStartup = AutoHideStartup;
_config.guiItem.autoUpdateInterval = autoUpdateInterval;
_config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate;
_config.uiItem.enableDragDropSort = EnableDragDropSort;
_config.uiItem.doubleClick2Activate = DoubleClick2Activate;
_config.guiItem.trayMenuServersLimit = trayMenuServersLimit;
_config.uiItem.currentFontFamily = currentFontFamily;
_config.speedTestItem.speedTestTimeout = SpeedTestTimeout;
_config.speedTestItem.speedTestUrl = SpeedTestUrl;
_config.guiItem.enableHWA = EnableHWA;
_config.constItem.subConvertUrl = SubConvertUrl;
//systemProxy
_config.systemProxyExceptions = systemProxyExceptions;
_config.systemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
//tun mode
_config.tunModeItem.strictRoute = TunStrictRoute;
_config.tunModeItem.stack = TunStack;
_config.tunModeItem.mtu = TunMtu;
//coreType
SaveCoreType();
if (ConfigHandler.SaveConfig(ref _config) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
}
} }
private int SaveCoreType() //if (Utils.IsNullOrEmpty(Kcpmtu.ToString()) || !Utils.IsNumberic(Kcpmtu.ToString())
// || Utils.IsNullOrEmpty(Kcptti.ToString()) || !Utils.IsNumberic(Kcptti.ToString())
// || Utils.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utils.IsNumberic(KcpuplinkCapacity.ToString())
// || Utils.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utils.IsNumberic(KcpdownlinkCapacity.ToString())
// || Utils.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utils.IsNumberic(KcpreadBufferSize.ToString())
// || Utils.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utils.IsNumberic(KcpwriteBufferSize.ToString()))
//{
// UI.Show(ResUI.FillKcpParameters);
// return;
//}
//Core
_config.inbound[0].localPort = localPort;
_config.inbound[0].udpEnabled = udpEnabled;
_config.inbound[0].sniffingEnabled = sniffingEnabled;
_config.inbound[0].routeOnly = routeOnly;
_config.inbound[0].allowLANConn = allowLANConn;
_config.inbound[0].newPort4LAN = newPort4LAN;
_config.inbound[0].user = user;
_config.inbound[0].pass = pass;
if (_config.inbound.Count > 1)
{ {
for (int k = 1; k <= _config.coreTypeItem.Count; k++) _config.inbound.RemoveAt(1);
{ }
var item = _config.coreTypeItem[k - 1]; _config.coreBasicItem.logEnabled = logEnabled;
var type = string.Empty; _config.coreBasicItem.loglevel = loglevel;
switch ((int)item.configType) _config.coreBasicItem.muxEnabled = muxEnabled;
{ _config.coreBasicItem.defAllowInsecure = defAllowInsecure;
case 1: _config.coreBasicItem.defFingerprint = defFingerprint;
type = CoreType1; _config.coreBasicItem.defUserAgent = defUserAgent;
break; _config.mux4Sbox.protocol = mux4SboxProtocol;
case 2: //Kcp
type = CoreType2; //_config.kcpItem.mtu = Kcpmtu;
break; //_config.kcpItem.tti = Kcptti;
//_config.kcpItem.uplinkCapacity = KcpuplinkCapacity;
//_config.kcpItem.downlinkCapacity = KcpdownlinkCapacity;
//_config.kcpItem.readBufferSize = KcpreadBufferSize;
//_config.kcpItem.writeBufferSize = KcpwriteBufferSize;
//_config.kcpItem.congestion = Kcpcongestion;
case 3: //UI
type = CoreType3; Utils.SetAutoRun(AutoRun);
break; _config.guiItem.autoRun = AutoRun;
_config.guiItem.enableStatistics = EnableStatistics;
_config.guiItem.keepOlderDedupl = KeepOlderDedupl;
_config.guiItem.ignoreGeoUpdateCore = IgnoreGeoUpdateCore;
_config.uiItem.enableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth;
_config.guiItem.enableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
_config.uiItem.autoHideStartup = AutoHideStartup;
_config.guiItem.autoUpdateInterval = autoUpdateInterval;
_config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate;
_config.uiItem.enableDragDropSort = EnableDragDropSort;
_config.uiItem.doubleClick2Activate = DoubleClick2Activate;
_config.guiItem.trayMenuServersLimit = trayMenuServersLimit;
_config.uiItem.currentFontFamily = currentFontFamily;
_config.speedTestItem.speedTestTimeout = SpeedTestTimeout;
_config.speedTestItem.speedTestUrl = SpeedTestUrl;
_config.guiItem.enableHWA = EnableHWA;
_config.constItem.subConvertUrl = SubConvertUrl;
case 4: //systemProxy
type = CoreType4; _config.systemProxyExceptions = systemProxyExceptions;
break; _config.systemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
case 5: //tun mode
type = CoreType5; _config.tunModeItem.strictRoute = TunStrictRoute;
break; _config.tunModeItem.stack = TunStack;
_config.tunModeItem.mtu = TunMtu;
case 6: //coreType
type = CoreType6; SaveCoreType();
break;
} if (ConfigHandler.SaveConfig(ref _config) == 0)
item.coreType = (ECoreType)Enum.Parse(typeof(ECoreType), type); {
} _noticeHandler?.Enqueue(ResUI.OperationSuccess);
return 0; _view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
} }
} }
private int SaveCoreType()
{
for (int k = 1; k <= _config.coreTypeItem.Count; k++)
{
var item = _config.coreTypeItem[k - 1];
var type = string.Empty;
switch ((int)item.configType)
{
case 1:
type = CoreType1;
break;
case 2:
type = CoreType2;
break;
case 3:
type = CoreType3;
break;
case 4:
type = CoreType4;
break;
case 5:
type = CoreType5;
break;
case 6:
type = CoreType6;
break;
}
item.coreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
}
return 0;
}
} }

View file

@ -8,98 +8,97 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class RoutingRuleDetailsViewModel : ReactiveObject
{ {
public class RoutingRuleDetailsViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
public IList<string> ProtocolItems { get; set; }
public IList<string> InboundTagItems { get; set; }
[Reactive]
public RulesItem SelectedSource { get; set; }
[Reactive]
public string Domain { get; set; }
[Reactive]
public string IP { get; set; }
[Reactive]
public string Process { get; set; }
[Reactive]
public bool AutoSort { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
public IList<string> ProtocolItems { get; set; } if (rulesItem.id.IsNullOrEmpty())
public IList<string> InboundTagItems { get; set; }
[Reactive]
public RulesItem SelectedSource { get; set; }
[Reactive]
public string Domain { get; set; }
[Reactive]
public string IP { get; set; }
[Reactive]
public string Process { get; set; }
[Reactive]
public bool AutoSort { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); rulesItem.id = Utils.GetGUID(false);
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); rulesItem.outboundTag = Global.agentTag;
_view = view; rulesItem.enabled = true;
SelectedSource = rulesItem;
if (rulesItem.id.IsNullOrEmpty()) }
{ else
rulesItem.id = Utils.GetGUID(false); {
rulesItem.outboundTag = Global.agentTag; SelectedSource = rulesItem;
rulesItem.enabled = true;
SelectedSource = rulesItem;
}
else
{
SelectedSource = rulesItem;
}
Domain = Utils.List2String(SelectedSource.domain, true);
IP = Utils.List2String(SelectedSource.ip, true);
Process = Utils.List2String(SelectedSource.process, true);
SaveCmd = ReactiveCommand.Create(() =>
{
SaveRules();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
private void SaveRules() Domain = Utils.List2String(SelectedSource.domain, true);
IP = Utils.List2String(SelectedSource.ip, true);
Process = Utils.List2String(SelectedSource.process, true);
SaveCmd = ReactiveCommand.Create(() =>
{ {
Domain = Utils.Convert2Comma(Domain); SaveRules();
IP = Utils.Convert2Comma(IP); });
Process = Utils.Convert2Comma(Process);
if (AutoSort) Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
{ }
SelectedSource.domain = Utils.String2ListSorted(Domain);
SelectedSource.ip = Utils.String2ListSorted(IP);
SelectedSource.process = Utils.String2ListSorted(Process);
}
else
{
SelectedSource.domain = Utils.String2List(Domain);
SelectedSource.ip = Utils.String2List(IP);
SelectedSource.process = Utils.String2List(Process);
}
SelectedSource.protocol = ProtocolItems?.ToList();
SelectedSource.inboundTag = InboundTagItems?.ToList();
bool hasRule = SelectedSource.domain?.Count > 0 private void SaveRules()
|| SelectedSource.ip?.Count > 0 {
|| SelectedSource.protocol?.Count > 0 Domain = Utils.Convert2Comma(Domain);
|| SelectedSource.process?.Count > 0 IP = Utils.Convert2Comma(IP);
|| !Utils.IsNullOrEmpty(SelectedSource.port); Process = Utils.Convert2Comma(Process);
if (!hasRule) if (AutoSort)
{ {
UI.ShowWarning(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process")); SelectedSource.domain = Utils.String2ListSorted(Domain);
return; SelectedSource.ip = Utils.String2ListSorted(IP);
} SelectedSource.process = Utils.String2ListSorted(Process);
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
} }
else
{
SelectedSource.domain = Utils.String2List(Domain);
SelectedSource.ip = Utils.String2List(IP);
SelectedSource.process = Utils.String2List(Process);
}
SelectedSource.protocol = ProtocolItems?.ToList();
SelectedSource.inboundTag = InboundTagItems?.ToList();
bool hasRule = SelectedSource.domain?.Count > 0
|| SelectedSource.ip?.Count > 0
|| SelectedSource.protocol?.Count > 0
|| SelectedSource.process?.Count > 0
|| !Utils.IsNullOrEmpty(SelectedSource.port);
if (!hasRule)
{
UI.ShowWarning(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process"));
return;
}
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
} }
} }

View file

@ -12,349 +12,348 @@ using v2rayN.Resx;
using v2rayN.Views; using v2rayN.Views;
using Application = System.Windows.Application; using Application = System.Windows.Application;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class RoutingRuleSettingViewModel : ReactiveObject
{ {
public class RoutingRuleSettingViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
private List<RulesItem> _rules;
[Reactive]
public RoutingItem SelectedRouting { get; set; }
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
[Reactive]
public RulesItemModel SelectedSource { get; set; }
public IList<RulesItemModel> SelectedSources { get; set; }
public ReactiveCommand<Unit, Unit> RuleAddCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromFileCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromUrlCmd { get; }
public ReactiveCommand<Unit, Unit> RuleRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RuleExportSelectedCmd { get; }
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
private List<RulesItem> _rules; SelectedSource = new();
[Reactive] if (routingItem.id.IsNullOrEmpty())
public RoutingItem SelectedRouting { get; set; }
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
[Reactive]
public RulesItemModel SelectedSource { get; set; }
public IList<RulesItemModel> SelectedSources { get; set; }
public ReactiveCommand<Unit, Unit> RuleAddCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromFileCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromUrlCmd { get; }
public ReactiveCommand<Unit, Unit> RuleRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RuleExportSelectedCmd { get; }
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); SelectedRouting = routingItem;
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); _rules = new();
_view = view; }
SelectedSource = new(); else
{
if (routingItem.id.IsNullOrEmpty()) SelectedRouting = routingItem;
{ _rules = Utils.FromJson<List<RulesItem>>(SelectedRouting.ruleSet);
SelectedRouting = routingItem;
_rules = new();
}
else
{
SelectedRouting = routingItem;
_rules = Utils.FromJson<List<RulesItem>>(SelectedRouting.ruleSet);
}
RefreshRulesItems();
var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty());
RuleAddCmd = ReactiveCommand.Create(() =>
{
RuleEdit(true);
});
ImportRulesFromFileCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromFile();
});
ImportRulesFromClipboardCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromClipboard();
});
ImportRulesFromUrlCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromUrl();
});
RuleRemoveCmd = ReactiveCommand.Create(() =>
{
RuleRemove();
}, canEditRemove);
RuleExportSelectedCmd = ReactiveCommand.Create(() =>
{
RuleExportSelected();
}, canEditRemove);
MoveTopCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Top);
}, canEditRemove);
MoveUpCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Up);
}, canEditRemove);
MoveDownCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Down);
}, canEditRemove);
MoveBottomCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Bottom);
}, canEditRemove);
SaveCmd = ReactiveCommand.Create(() =>
{
SaveRouting();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
public void RefreshRulesItems() RefreshRulesItems();
{
_rulesItems.Clear();
foreach (var item in _rules) var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty());
RuleAddCmd = ReactiveCommand.Create(() =>
{
RuleEdit(true);
});
ImportRulesFromFileCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromFile();
});
ImportRulesFromClipboardCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromClipboard();
});
ImportRulesFromUrlCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromUrl();
});
RuleRemoveCmd = ReactiveCommand.Create(() =>
{
RuleRemove();
}, canEditRemove);
RuleExportSelectedCmd = ReactiveCommand.Create(() =>
{
RuleExportSelected();
}, canEditRemove);
MoveTopCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Top);
}, canEditRemove);
MoveUpCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Up);
}, canEditRemove);
MoveDownCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Down);
}, canEditRemove);
MoveBottomCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Bottom);
}, canEditRemove);
SaveCmd = ReactiveCommand.Create(() =>
{
SaveRouting();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
public void RefreshRulesItems()
{
_rulesItems.Clear();
foreach (var item in _rules)
{
var it = new RulesItemModel()
{ {
var it = new RulesItemModel() id = item.id,
{ outboundTag = item.outboundTag,
id = item.id, port = item.port,
outboundTag = item.outboundTag, protocols = Utils.List2String(item.protocol),
port = item.port, inboundTags = Utils.List2String(item.inboundTag),
protocols = Utils.List2String(item.protocol), domains = Utils.List2String(item.domain),
inboundTags = Utils.List2String(item.inboundTag), ips = Utils.List2String(item.ip),
domains = Utils.List2String(item.domain), enabled = item.enabled,
ips = Utils.List2String(item.ip), };
enabled = item.enabled, _rulesItems.Add(it);
}; }
_rulesItems.Add(it); }
public void RuleEdit(bool blNew)
{
RulesItem item;
if (blNew)
{
item = new();
}
else
{
item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item is null)
{
return;
} }
} }
var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog();
public void RuleEdit(bool blNew) if (ret == true)
{ {
RulesItem item;
if (blNew) if (blNew)
{ {
item = new(); _rules.Add(item);
} }
else
{
item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item is null)
{
return;
}
}
var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog();
if (ret == true)
{
if (blNew)
{
_rules.Add(item);
}
RefreshRulesItems();
}
}
public void RuleRemove()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{
return;
}
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
_rules.Remove(item);
}
}
RefreshRulesItems(); RefreshRulesItems();
} }
public void RuleExportSelected()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var lst = new List<RulesItem>();
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
lst.Add(item);
}
}
if (lst.Count > 0)
{
Utils.SetClipboardData(Utils.ToJson(lst));
//UI.Show(ResUI.OperationSuccess"));
}
}
public void MoveRule(EMove eMove)
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item == null)
{
return;
}
var index = _rules.IndexOf(item);
if (ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0)
{
RefreshRulesItems();
}
}
private void SaveRouting()
{
string remarks = SelectedRouting.remarks;
if (Utils.IsNullOrEmpty(remarks))
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
var item = SelectedRouting;
foreach (var it in _rules)
{
it.id = Utils.GetGUID(false);
}
item.ruleNum = _rules.Count;
item.ruleSet = Utils.ToJson(_rules, false);
if (ConfigHandler.SaveRoutingItem(ref _config, item) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
}
}
#region Import rules
private void ImportRulesFromFile()
{
OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = false,
Filter = "Rules|*.json|All|*.*"
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
string result = Utils.LoadResource(fileName);
if (Utils.IsNullOrEmpty(result))
{
return;
}
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private void ImportRulesFromClipboard()
{
string clipboardData = Utils.GetClipboardData();
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private async Task ImportRulesFromUrl()
{
var url = SelectedRouting.url;
if (Utils.IsNullOrEmpty(url))
{
UI.Show(ResUI.MsgNeedUrl);
return;
}
DownloadHandle downloadHandle = new DownloadHandle();
string result = await downloadHandle.TryDownloadString(url, true, "");
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
RefreshRulesItems();
}));
UI.Show(ResUI.OperationSuccess);
}
}
private int AddBatchRoutingRules(RoutingItem routingItem, string clipboardData)
{
bool blReplace = false;
if (UI.ShowYesNo(ResUI.AddBatchRoutingRulesYesNo) == MessageBoxResult.No)
{
blReplace = true;
}
if (Utils.IsNullOrEmpty(clipboardData))
{
return -1;
}
var lstRules = Utils.FromJson<List<RulesItem>>(clipboardData);
if (lstRules == null)
{
return -1;
}
foreach (var rule in lstRules)
{
rule.id = Utils.GetGUID(false);
}
if (blReplace)
{
_rules = lstRules;
}
else
{
_rules.AddRange(lstRules);
}
return 0;
}
#endregion Import rules
} }
public void RuleRemove()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{
return;
}
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
_rules.Remove(item);
}
}
RefreshRulesItems();
}
public void RuleExportSelected()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var lst = new List<RulesItem>();
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
lst.Add(item);
}
}
if (lst.Count > 0)
{
Utils.SetClipboardData(Utils.ToJson(lst));
//UI.Show(ResUI.OperationSuccess"));
}
}
public void MoveRule(EMove eMove)
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item == null)
{
return;
}
var index = _rules.IndexOf(item);
if (ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0)
{
RefreshRulesItems();
}
}
private void SaveRouting()
{
string remarks = SelectedRouting.remarks;
if (Utils.IsNullOrEmpty(remarks))
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
var item = SelectedRouting;
foreach (var it in _rules)
{
it.id = Utils.GetGUID(false);
}
item.ruleNum = _rules.Count;
item.ruleSet = Utils.ToJson(_rules, false);
if (ConfigHandler.SaveRoutingItem(ref _config, item) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
}
}
#region Import rules
private void ImportRulesFromFile()
{
OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = false,
Filter = "Rules|*.json|All|*.*"
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
string result = Utils.LoadResource(fileName);
if (Utils.IsNullOrEmpty(result))
{
return;
}
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private void ImportRulesFromClipboard()
{
string clipboardData = Utils.GetClipboardData();
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private async Task ImportRulesFromUrl()
{
var url = SelectedRouting.url;
if (Utils.IsNullOrEmpty(url))
{
UI.Show(ResUI.MsgNeedUrl);
return;
}
DownloadHandle downloadHandle = new DownloadHandle();
string result = await downloadHandle.TryDownloadString(url, true, "");
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
RefreshRulesItems();
}));
UI.Show(ResUI.OperationSuccess);
}
}
private int AddBatchRoutingRules(RoutingItem routingItem, string clipboardData)
{
bool blReplace = false;
if (UI.ShowYesNo(ResUI.AddBatchRoutingRulesYesNo) == MessageBoxResult.No)
{
blReplace = true;
}
if (Utils.IsNullOrEmpty(clipboardData))
{
return -1;
}
var lstRules = Utils.FromJson<List<RulesItem>>(clipboardData);
if (lstRules == null)
{
return -1;
}
foreach (var rule in lstRules)
{
rule.id = Utils.GetGUID(false);
}
if (blReplace)
{
_rules = lstRules;
}
else
{
_rules.AddRange(lstRules);
}
return 0;
}
#endregion Import rules
} }

View file

@ -10,299 +10,298 @@ using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
using v2rayN.Views; using v2rayN.Views;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class RoutingSettingViewModel : ReactiveObject
{ {
public class RoutingSettingViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
private RoutingItem _lockedItem;
private List<RulesItem> _lockedRules;
#region Reactive
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
[Reactive]
public RoutingItemModel SelectedSource { get; set; }
public IList<RoutingItemModel> SelectedSources { get; set; }
[Reactive]
public bool enableRoutingAdvanced { get; set; }
[Reactive]
public bool enableRoutingBasic { get; set; }
[Reactive]
public string domainStrategy { get; set; }
[Reactive]
public string domainMatcher { get; set; }
[Reactive]
public string domainStrategy4Singbox { get; set; }
[Reactive]
public string ProxyDomain { get; set; }
[Reactive]
public string ProxyIP { get; set; }
[Reactive]
public string DirectDomain { get; set; }
[Reactive]
public string DirectIP { get; set; }
[Reactive]
public string BlockDomain { get; set; }
[Reactive]
public string BlockIP { get; set; }
public ReactiveCommand<Unit, Unit> RoutingBasicImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public bool IsModified { get; set; }
#endregion Reactive
public RoutingSettingViewModel(Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
private RoutingItem _lockedItem; SelectedSource = new();
private List<RulesItem> _lockedRules;
#region Reactive ConfigHandler.InitBuiltinRouting(ref _config);
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>(); enableRoutingAdvanced = _config.routingBasicItem.enableRoutingAdvanced;
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems; domainStrategy = _config.routingBasicItem.domainStrategy;
domainMatcher = _config.routingBasicItem.domainMatcher;
domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox;
[Reactive] RefreshRoutingItems();
public RoutingItemModel SelectedSource { get; set; }
public IList<RoutingItemModel> SelectedSources { get; set; } BindingLockedData();
[Reactive] var canEditRemove = this.WhenAnyValue(
public bool enableRoutingAdvanced { get; set; } x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty());
[Reactive] this.WhenAnyValue(
public bool enableRoutingBasic { get; set; } x => x.enableRoutingAdvanced)
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
[Reactive] RoutingBasicImportRulesCmd = ReactiveCommand.Create(() =>
public string domainStrategy { get; set; }
[Reactive]
public string domainMatcher { get; set; }
[Reactive]
public string domainStrategy4Singbox { get; set; }
[Reactive]
public string ProxyDomain { get; set; }
[Reactive]
public string ProxyIP { get; set; }
[Reactive]
public string DirectDomain { get; set; }
[Reactive]
public string DirectIP { get; set; }
[Reactive]
public string BlockDomain { get; set; }
[Reactive]
public string BlockIP { get; set; }
public ReactiveCommand<Unit, Unit> RoutingBasicImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public bool IsModified { get; set; }
#endregion Reactive
public RoutingSettingViewModel(Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); RoutingBasicImportRules();
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); });
_view = view;
SelectedSource = new();
ConfigHandler.InitBuiltinRouting(ref _config); RoutingAdvancedAddCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedEdit(true);
});
RoutingAdvancedRemoveCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedRemove();
}, canEditRemove);
RoutingAdvancedSetDefaultCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedSetDefault();
}, canEditRemove);
RoutingAdvancedImportRulesCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedImportRules();
});
enableRoutingAdvanced = _config.routingBasicItem.enableRoutingAdvanced; SaveCmd = ReactiveCommand.Create(() =>
domainStrategy = _config.routingBasicItem.domainStrategy; {
domainMatcher = _config.routingBasicItem.domainMatcher; SaveRouting();
domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox; });
RefreshRoutingItems(); Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
BindingLockedData(); #region locked
var canEditRemove = this.WhenAnyValue( private void BindingLockedData()
x => x.SelectedSource, {
selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty()); _lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config);
if (_lockedItem != null)
{
_lockedRules = Utils.FromJson<List<RulesItem>>(_lockedItem.ruleSet);
ProxyDomain = Utils.List2String(_lockedRules[0].domain, true);
ProxyIP = Utils.List2String(_lockedRules[0].ip, true);
this.WhenAnyValue( DirectDomain = Utils.List2String(_lockedRules[1].domain, true);
x => x.enableRoutingAdvanced) DirectIP = Utils.List2String(_lockedRules[1].ip, true);
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
RoutingBasicImportRulesCmd = ReactiveCommand.Create(() => BlockDomain = Utils.List2String(_lockedRules[2].domain, true);
{ BlockIP = Utils.List2String(_lockedRules[2].ip, true);
RoutingBasicImportRules();
});
RoutingAdvancedAddCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedEdit(true);
});
RoutingAdvancedRemoveCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedRemove();
}, canEditRemove);
RoutingAdvancedSetDefaultCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedSetDefault();
}, canEditRemove);
RoutingAdvancedImportRulesCmd = ReactiveCommand.Create(() =>
{
RoutingAdvancedImportRules();
});
SaveCmd = ReactiveCommand.Create(() =>
{
SaveRouting();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
}
#region locked private void EndBindingLockedData()
{
private void BindingLockedData() if (_lockedItem != null)
{ {
_lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); _lockedRules[0].domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx()));
if (_lockedItem != null) _lockedRules[0].ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx()));
{
_lockedRules = Utils.FromJson<List<RulesItem>>(_lockedItem.ruleSet);
ProxyDomain = Utils.List2String(_lockedRules[0].domain, true);
ProxyIP = Utils.List2String(_lockedRules[0].ip, true);
DirectDomain = Utils.List2String(_lockedRules[1].domain, true); _lockedRules[1].domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx()));
DirectIP = Utils.List2String(_lockedRules[1].ip, true); _lockedRules[1].ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx()));
BlockDomain = Utils.List2String(_lockedRules[2].domain, true); _lockedRules[2].domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx()));
BlockIP = Utils.List2String(_lockedRules[2].ip, true); _lockedRules[2].ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx()));
}
_lockedItem.ruleSet = Utils.ToJson(_lockedRules, false);
ConfigHandler.SaveRoutingItem(ref _config, _lockedItem);
} }
}
private void EndBindingLockedData() #endregion locked
#region Refresh Save
public void RefreshRoutingItems()
{
_routingItems.Clear();
var routings = LazyConfig.Instance.RoutingItems();
foreach (var item in routings)
{ {
if (_lockedItem != null) bool def = false;
if (item.id == _config.routingBasicItem.routingIndexId)
{ {
_lockedRules[0].domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx())); def = true;
_lockedRules[0].ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx()));
_lockedRules[1].domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx()));
_lockedRules[1].ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx()));
_lockedRules[2].domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx()));
_lockedRules[2].ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx()));
_lockedItem.ruleSet = Utils.ToJson(_lockedRules, false);
ConfigHandler.SaveRoutingItem(ref _config, _lockedItem);
} }
var it = new RoutingItemModel()
{
isActive = def,
ruleNum = item.ruleNum,
id = item.id,
remarks = item.remarks,
url = item.url,
customIcon = item.customIcon,
sort = item.sort,
};
_routingItems.Add(it);
} }
}
#endregion locked private void SaveRouting()
{
_config.routingBasicItem.domainStrategy = domainStrategy;
_config.routingBasicItem.enableRoutingAdvanced = enableRoutingAdvanced;
_config.routingBasicItem.domainMatcher = domainMatcher;
_config.routingBasicItem.domainStrategy4Singbox = domainStrategy4Singbox;
#region Refresh Save EndBindingLockedData();
public void RefreshRoutingItems() if (ConfigHandler.SaveConfig(ref _config) == 0)
{ {
_routingItems.Clear(); _noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
var routings = LazyConfig.Instance.RoutingItems();
foreach (var item in routings)
{
bool def = false;
if (item.id == _config.routingBasicItem.routingIndexId)
{
def = true;
}
var it = new RoutingItemModel()
{
isActive = def,
ruleNum = item.ruleNum,
id = item.id,
remarks = item.remarks,
url = item.url,
customIcon = item.customIcon,
sort = item.sort,
};
_routingItems.Add(it);
}
} }
else
private void SaveRouting()
{ {
_config.routingBasicItem.domainStrategy = domainStrategy; UI.ShowWarning(ResUI.OperationFailed);
_config.routingBasicItem.enableRoutingAdvanced = enableRoutingAdvanced;
_config.routingBasicItem.domainMatcher = domainMatcher;
_config.routingBasicItem.domainStrategy4Singbox = domainStrategy4Singbox;
EndBindingLockedData();
if (ConfigHandler.SaveConfig(ref _config) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
}
} }
}
#endregion Refresh Save #endregion Refresh Save
private void RoutingBasicImportRules() private void RoutingBasicImportRules()
{
//Extra to bypass the mainland
ProxyDomain = "geosite:google";
DirectDomain = "geosite:cn";
DirectIP = "geoip:private,geoip:cn";
BlockDomain = "geosite:category-ads-all";
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
UI.Show(ResUI.OperationSuccess);
}
public void RoutingAdvancedEdit(bool blNew)
{
RoutingItem item;
if (blNew)
{ {
//Extra to bypass the mainland item = new();
ProxyDomain = "geosite:google";
DirectDomain = "geosite:cn";
DirectIP = "geoip:private,geoip:cn";
BlockDomain = "geosite:category-ads-all";
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
UI.Show(ResUI.OperationSuccess);
} }
else
public void RoutingAdvancedEdit(bool blNew)
{ {
RoutingItem item; item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id);
if (blNew) if (item is null)
{
item = new();
}
else
{
item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id);
if (item is null)
{
return;
}
}
var ret = (new RoutingRuleSettingWindow(item)).ShowDialog();
if (ret == true)
{
RefreshRoutingItems();
IsModified = true;
}
}
public void RoutingAdvancedRemove()
{
if (SelectedSource is null || SelectedSource.remarks.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{ {
return; return;
} }
foreach (var it in SelectedSources) }
{ var ret = (new RoutingRuleSettingWindow(item)).ShowDialog();
var item = LazyConfig.Instance.GetRoutingItem(it?.id); if (ret == true)
if (item != null) {
{
ConfigHandler.RemoveRoutingItem(item);
}
}
RefreshRoutingItems(); RefreshRoutingItems();
IsModified = true; IsModified = true;
} }
}
public void RoutingAdvancedSetDefault() public void RoutingAdvancedRemove()
{
if (SelectedSource is null || SelectedSource.remarks.IsNullOrEmpty())
{ {
var item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id); UI.Show(ResUI.PleaseSelectRules);
if (item is null) return;
}
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{
return;
}
foreach (var it in SelectedSources)
{
var item = LazyConfig.Instance.GetRoutingItem(it?.id);
if (item != null)
{ {
UI.Show(ResUI.PleaseSelectRules); ConfigHandler.RemoveRoutingItem(item);
return;
}
if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0)
{
RefreshRoutingItems();
IsModified = true;
} }
} }
private void RoutingAdvancedImportRules() RefreshRoutingItems();
IsModified = true;
}
public void RoutingAdvancedSetDefault()
{
var item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id);
if (item is null)
{ {
if (ConfigHandler.InitBuiltinRouting(ref _config, true) == 0) UI.Show(ResUI.PleaseSelectRules);
{ return;
RefreshRoutingItems(); }
IsModified = true;
} if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0)
{
RefreshRoutingItems();
IsModified = true;
}
}
private void RoutingAdvancedImportRules()
{
if (ConfigHandler.InitBuiltinRouting(ref _config, true) == 0)
{
RefreshRoutingItems();
IsModified = true;
} }
} }
} }

View file

@ -8,79 +8,78 @@ using v2rayN.Handler;
using v2rayN.Mode; using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class SubEditViewModel : ReactiveObject
{ {
public class SubEditViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
[Reactive]
public SubItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public SubEditViewModel(SubItem subItem, Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private Window _view; _view = view;
[Reactive] if (subItem.id.IsNullOrEmpty())
public SubItem SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public SubEditViewModel(SubItem subItem, Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); SelectedSource = subItem;
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); }
_view = view; else
{
if (subItem.id.IsNullOrEmpty()) SelectedSource = Utils.DeepCopy(subItem);
{
SelectedSource = subItem;
}
else
{
SelectedSource = Utils.DeepCopy(subItem);
}
SaveCmd = ReactiveCommand.Create(() =>
{
SaveSub();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
private void SaveSub() SaveCmd = ReactiveCommand.Create(() =>
{ {
string remarks = SelectedSource.remarks; SaveSub();
if (string.IsNullOrEmpty(remarks)) });
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
var item = LazyConfig.Instance.GetSubItem(SelectedSource.id); Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
if (item is null) }
{
item = SelectedSource;
}
else
{
item.remarks = SelectedSource.remarks;
item.url = SelectedSource.url;
item.moreUrl = SelectedSource.moreUrl;
item.enabled = SelectedSource.enabled;
item.autoUpdateInterval = SelectedSource.autoUpdateInterval;
item.userAgent = SelectedSource.userAgent;
item.sort = SelectedSource.sort;
item.filter = SelectedSource.filter;
item.convertTarget = SelectedSource.convertTarget;
}
if (ConfigHandler.AddSubItem(ref _config, item) == 0) private void SaveSub()
{ {
_noticeHandler?.Enqueue(ResUI.OperationSuccess); string remarks = SelectedSource.remarks;
_view.DialogResult = true; if (string.IsNullOrEmpty(remarks))
//_view?.Close(); {
} UI.Show(ResUI.PleaseFillRemarks);
else return;
{ }
_noticeHandler?.Enqueue(ResUI.OperationFailed);
} var item = LazyConfig.Instance.GetSubItem(SelectedSource.id);
if (item is null)
{
item = SelectedSource;
}
else
{
item.remarks = SelectedSource.remarks;
item.url = SelectedSource.url;
item.moreUrl = SelectedSource.moreUrl;
item.enabled = SelectedSource.enabled;
item.autoUpdateInterval = SelectedSource.autoUpdateInterval;
item.userAgent = SelectedSource.userAgent;
item.sort = SelectedSource.sort;
item.filter = SelectedSource.filter;
item.convertTarget = SelectedSource.convertTarget;
}
if (ConfigHandler.AddSubItem(ref _config, item) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
//_view?.Close();
}
else
{
_noticeHandler?.Enqueue(ResUI.OperationFailed);
} }
} }
} }

View file

@ -12,119 +12,118 @@ using v2rayN.Mode;
using v2rayN.Resx; using v2rayN.Resx;
using v2rayN.Views; using v2rayN.Views;
namespace v2rayN.ViewModels namespace v2rayN.ViewModels;
public class SubSettingViewModel : ReactiveObject
{ {
public class SubSettingViewModel : ReactiveObject private static Config _config;
private NoticeHandler? _noticeHandler;
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
public IObservableCollection<SubItem> SubItems => _subItems;
[Reactive]
public SubItem SelectedSource { get; set; }
public IList<SubItem> SelectedSources { get; set; }
public ReactiveCommand<Unit, Unit> SubAddCmd { get; }
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
public bool IsModified { get; set; }
public SubSettingViewModel(Window view)
{ {
private static Config _config; _config = LazyConfig.Instance.GetConfig();
private NoticeHandler? _noticeHandler; _noticeHandler = Locator.Current.GetService<NoticeHandler>();
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>(); SelectedSource = new();
public IObservableCollection<SubItem> SubItems => _subItems;
[Reactive] RefreshSubItems();
public SubItem SelectedSource { get; set; }
public IList<SubItem> SelectedSources { get; set; } var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty());
public ReactiveCommand<Unit, Unit> SubAddCmd { get; } SubAddCmd = ReactiveCommand.Create(() =>
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
public bool IsModified { get; set; }
public SubSettingViewModel(Window view)
{ {
_config = LazyConfig.Instance.GetConfig(); EditSub(true);
_noticeHandler = Locator.Current.GetService<NoticeHandler>(); });
SubDeleteCmd = ReactiveCommand.Create(() =>
{
DeleteSub();
}, canEditRemove);
SubEditCmd = ReactiveCommand.Create(() =>
{
EditSub(false);
}, canEditRemove);
SubShareCmd = ReactiveCommand.Create(() =>
{
SubShare();
}, canEditRemove);
SelectedSource = new(); Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
RefreshSubItems(); public void RefreshSubItems()
{
_subItems.Clear();
_subItems.AddRange(LazyConfig.Instance.SubItems().OrderBy(t => t.sort));
}
var canEditRemove = this.WhenAnyValue( public void EditSub(bool blNew)
x => x.SelectedSource, {
selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty()); SubItem item;
if (blNew)
SubAddCmd = ReactiveCommand.Create(() => {
{ item = new();
EditSub(true);
});
SubDeleteCmd = ReactiveCommand.Create(() =>
{
DeleteSub();
}, canEditRemove);
SubEditCmd = ReactiveCommand.Create(() =>
{
EditSub(false);
}, canEditRemove);
SubShareCmd = ReactiveCommand.Create(() =>
{
SubShare();
}, canEditRemove);
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
} }
else
public void RefreshSubItems()
{ {
_subItems.Clear(); item = LazyConfig.Instance.GetSubItem(SelectedSource?.id);
_subItems.AddRange(LazyConfig.Instance.SubItems().OrderBy(t => t.sort)); if (item is null)
}
public void EditSub(bool blNew)
{
SubItem item;
if (blNew)
{
item = new();
}
else
{
item = LazyConfig.Instance.GetSubItem(SelectedSource?.id);
if (item is null)
{
return;
}
}
var ret = (new SubEditWindow(item)).ShowDialog();
if (ret == true)
{
RefreshSubItems();
IsModified = true;
}
}
private void DeleteSub()
{
if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No)
{ {
return; return;
} }
}
foreach (var it in SelectedSources) var ret = (new SubEditWindow(item)).ShowDialog();
{ if (ret == true)
ConfigHandler.DeleteSubItem(ref _config, it?.id); {
}
RefreshSubItems(); RefreshSubItems();
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
IsModified = true; IsModified = true;
} }
}
private async void SubShare() private void DeleteSub()
{
if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No)
{ {
if (Utils.IsNullOrEmpty(SelectedSource?.url)) return;
{
return;
}
var img = QRCodeHelper.GetQRCode(SelectedSource?.url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
txtContent = { Text = SelectedSource?.url },
};
await DialogHost.Show(dialog, "SubDialog");
} }
foreach (var it in SelectedSources)
{
ConfigHandler.DeleteSubItem(ref _config, it?.id);
}
RefreshSubItems();
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
IsModified = true;
}
private async void SubShare()
{
if (Utils.IsNullOrEmpty(SelectedSource?.url))
{
return;
}
var img = QRCodeHelper.GetQRCode(SelectedSource?.url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
txtContent = { Text = SelectedSource?.url },
};
await DialogHost.Show(dialog, "SubDialog");
} }
} }

View file

@ -1,122 +1,115 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore> <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>v2rayN.ico</ApplicationIcon> <ApplicationIcon>v2rayN.ico</ApplicationIcon>
<Copyright>Copyright © 2017-2023 (GPLv3)</Copyright> <Copyright>Copyright © 2017-2023 (GPLv3)</Copyright>
<FileVersion>6.28</FileVersion> <FileVersion>6.28</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Downloader" Version="3.0.6" /> <PackageReference Include="Downloader" Version="3.0.6" />
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" /> <PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.108" /> <PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.115" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="QRCoder.Xaml" Version="1.4.3" /> <PackageReference Include="QRCoder.Xaml" Version="1.4.3" />
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" /> <PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
<PackageReference Include="TaskScheduler" Version="2.10.1" /> <PackageReference Include="TaskScheduler" Version="2.10.1" />
<PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.12" /> <PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.12" />
<PackageReference Include="ReactiveUI.Fody" Version="18.4.1" /> <PackageReference Include="ReactiveUI.Fody" Version="18.4.1" />
<PackageReference Include="ReactiveUI.Validation" Version="3.0.22" /> <PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
<PackageReference Include="ReactiveUI.WPF" Version="18.4.1" /> <PackageReference Include="ReactiveUI.WPF" Version="18.4.1" />
<PackageReference Include="Splat.NLog" Version="14.6.37" /> <PackageReference Include="Splat.NLog" Version="14.7.1" />
<PackageReference Include="System.Reactive" Version="6.0.0" /> <PackageReference Include="System.Reactive" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ItemGroup> <AdditionalFiles Include="app.manifest" />
<AdditionalFiles Include="app.manifest" /> <EmbeddedResource Include="Sample\SingboxSampleClientConfig">
<EmbeddedResource Include="Sample\SingboxSampleClientConfig"> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory> </EmbeddedResource>
</EmbeddedResource>
<EmbeddedResource Include="Sample\tun_singbox_dns"> <EmbeddedResource Include="Sample\tun_singbox_dns">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\custom_routing_black"> <EmbeddedResource Include="Sample\custom_routing_black">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\custom_routing_global"> <EmbeddedResource Include="Sample\custom_routing_global">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\custom_routing_locked"> <EmbeddedResource Include="Sample\custom_routing_locked">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\custom_routing_rules"> <EmbeddedResource Include="Sample\custom_routing_rules">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\custom_routing_white"> <EmbeddedResource Include="Sample\custom_routing_white">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\SampleClientConfig"> <EmbeddedResource Include="Sample\SampleClientConfig">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\SampleHttprequest"> <EmbeddedResource Include="Sample\SampleHttprequest">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\SampleHttpresponse"> <EmbeddedResource Include="Sample\SampleHttpresponse">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\SampleInbound"> <EmbeddedResource Include="Sample\SampleInbound">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\tun_singbox_rules"> <EmbeddedResource Include="Sample\tun_singbox_rules">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\tun_singbox_inbound"> <EmbeddedResource Include="Sample\tun_singbox_inbound">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\dns_v2ray_normal"> <EmbeddedResource Include="Sample\dns_v2ray_normal">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Sample\dns_singbox_normal"> <EmbeddedResource Include="Sample\dns_singbox_normal">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="v2rayN.ico"> <EmbeddedResource Include="v2rayN.ico">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<Resource Include="Resources\NotifyIcon1.ico" /> <Resource Include="Resources\NotifyIcon1.ico" />
<Resource Include="Resources\NotifyIcon2.ico" /> <Resource Include="Resources\NotifyIcon2.ico" />
<Resource Include="Resources\NotifyIcon3.ico" /> <Resource Include="Resources\NotifyIcon3.ico" />
<Resource Include="v2rayN.ico"> <Resource Include="v2rayN.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource> </Resource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ItemGroup> <ProjectReference Include="..\PacLib\PacLib.csproj" />
<ProjectReference Include="..\PacLib\PacLib.csproj" /> <ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
<ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resx\ResUI.Designer.cs"> <Compile Update="Resx\ResUI.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>ResUI.resx</DependentUpon> <DependentUpon>ResUI.resx</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Resx\ResUI.resx"> <EmbeddedResource Update="Resx\ResUI.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>ResUI.Designer.cs</LastGenOutput> <LastGenOutput>ResUI.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx"> <EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx"> <EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx"> <EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.ru.resx"> <EmbeddedResource Update="Resx\ResUI.ru.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project>
</Project>

View file

@ -6,143 +6,142 @@ using System.Text;
using System.Web; using System.Web;
using System.Windows.Forms; using System.Windows.Forms;
namespace v2rayUpgrade namespace v2rayUpgrade;
{
public partial class MainForm : Form
{
private readonly string defaultFilename = "v2ray-windows.zip";
private string? fileName;
public MainForm(string[] args) public partial class MainForm : Form
{
private readonly string defaultFilename = "v2ray-windows.zip";
private string? fileName;
public MainForm(string[] args)
{
InitializeComponent();
if (args.Length > 0)
{ {
InitializeComponent(); fileName = HttpUtility.UrlDecode(string.Join(" ", args));
if (args.Length > 0) }
else
{
fileName = defaultFilename;
}
}
private void ShowWarn(string message)
{
MessageBox.Show(message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
private void btnOK_Click(object sender, EventArgs e)
{
try
{
Process[] existing = Process.GetProcessesByName("v2rayN");
foreach (Process p in existing)
{ {
fileName = HttpUtility.UrlDecode(string.Join(" ", args)); string? path = p.MainModule?.FileName;
if (path == GetPath("v2rayN.exe"))
{
p.Kill();
p.WaitForExit(100);
}
} }
else }
catch (Exception ex)
{
// Access may be denied without admin right. The user may not be an administrator.
ShowWarn("Failed to close v2rayN(关闭v2rayN失败).\n" +
"Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN否则可能升级失败。\n\n" + ex.StackTrace);
}
if (!File.Exists(fileName))
{
if (File.Exists(defaultFilename))
{ {
fileName = defaultFilename; fileName = defaultFilename;
} }
} else
private void ShowWarn(string message)
{
MessageBox.Show(message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
private void btnOK_Click(object sender, EventArgs e)
{
try
{ {
Process[] existing = Process.GetProcessesByName("v2rayN"); ShowWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在).");
foreach (Process p in existing)
{
string? path = p.MainModule?.FileName;
if (path == GetPath("v2rayN.exe"))
{
p.Kill();
p.WaitForExit(100);
}
}
}
catch (Exception ex)
{
// Access may be denied without admin right. The user may not be an administrator.
ShowWarn("Failed to close v2rayN(关闭v2rayN失败).\n" +
"Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN否则可能升级失败。\n\n" + ex.StackTrace);
}
if (!File.Exists(fileName))
{
if (File.Exists(defaultFilename))
{
fileName = defaultFilename;
}
else
{
ShowWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在).");
return;
}
}
StringBuilder sb = new();
try
{
string thisAppOldFile = $"{Application.ExecutablePath}.tmp";
File.Delete(thisAppOldFile);
string startKey = "v2rayN/";
using ZipArchive archive = ZipFile.OpenRead(fileName);
foreach (ZipArchiveEntry entry in archive.Entries)
{
try
{
if (entry.Length == 0)
{
continue;
}
string fullName = entry.FullName;
if (fullName.StartsWith(startKey))
{
fullName = fullName[startKey.Length..];
}
if (string.Equals(Application.ExecutablePath, GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(Application.ExecutablePath, thisAppOldFile);
}
string entryOutputPath = GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
entry.ExtractToFile(entryOutputPath, true);
}
catch (Exception ex)
{
sb.Append(ex.StackTrace);
}
}
}
catch (Exception ex)
{
ShowWarn("Upgrade Failed(升级失败)." + ex.StackTrace);
return; return;
} }
if (sb.Length > 0) }
StringBuilder sb = new();
try
{
string thisAppOldFile = $"{Application.ExecutablePath}.tmp";
File.Delete(thisAppOldFile);
string startKey = "v2rayN/";
using ZipArchive archive = ZipFile.OpenRead(fileName);
foreach (ZipArchiveEntry entry in archive.Entries)
{ {
ShowWarn("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" + try
"(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString()); {
return; if (entry.Length == 0)
{
continue;
}
string fullName = entry.FullName;
if (fullName.StartsWith(startKey))
{
fullName = fullName[startKey.Length..];
}
if (string.Equals(Application.ExecutablePath, GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(Application.ExecutablePath, thisAppOldFile);
}
string entryOutputPath = GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
entry.ExtractToFile(entryOutputPath, true);
}
catch (Exception ex)
{
sb.Append(ex.StackTrace);
}
} }
Process.Start("v2rayN.exe");
MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
Close();
} }
catch (Exception ex)
private void btnClose_Click(object sender, EventArgs e)
{ {
Close(); ShowWarn("Upgrade Failed(升级失败)." + ex.StackTrace);
return;
}
if (sb.Length > 0)
{
ShowWarn("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" +
"(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString());
return;
} }
public static string GetExePath() Process.Start("v2rayN.exe");
{ MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
return Application.ExecutablePath;
}
public static string StartupPath() Close();
{ }
return Application.StartupPath;
}
public static string GetPath(string fileName) private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
public static string GetExePath()
{
return Application.ExecutablePath;
}
public static string StartupPath()
{
return Application.StartupPath;
}
public static string GetPath(string fileName)
{
string startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{ {
string startupPath = StartupPath(); return startupPath;
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
}
return Path.Combine(startupPath, fileName);
} }
return Path.Combine(startupPath, fileName);
} }
} }

View file

@ -1,20 +1,16 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
namespace v2rayUpgrade namespace v2rayUpgrade;
internal static class Program
{ {
internal static class Program [STAThread]
private static void Main(string[] args)
{ {
/// <summary> Application.EnableVisualStyles();
/// 应用程序的主入口点。 Application.SetHighDpiMode(HighDpiMode.SystemAware);
/// </summary> Application.SetCompatibleTextRenderingDefault(false);
[STAThread] Application.Run(new MainForm(args));
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(args));
}
} }
} }

View file

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<Copyright>Copyright © 2019-2023 (GPLv3)</Copyright> <Copyright>Copyright © 2019-2023 (GPLv3)</Copyright>
<FileVersion>1.1.0.0</FileVersion> <FileVersion>1.1.0.0</FileVersion>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
</Project> </Project>