diff --git a/Netch/Forms/GlobalBypassIPForm.cs b/Netch/Forms/GlobalBypassIPForm.cs index 9bdc34e7..94c3bb23 100644 --- a/Netch/Forms/GlobalBypassIPForm.cs +++ b/Netch/Forms/GlobalBypassIPForm.cs @@ -50,13 +50,13 @@ namespace Netch.Forms MessageBoxX.Show(i18N.Translate("Please select an IP")); } - private void ControlButton_Click(object sender, EventArgs e) + private async void ControlButton_Click(object sender, EventArgs e) { Global.Settings.TUNTAP.BypassIPs.Clear(); foreach (var ip in IPListBox.Items) Global.Settings.TUNTAP.BypassIPs.Add((string)ip); - Configuration.Save(); + await Configuration.SaveAsync(); MessageBoxX.Show(i18N.Translate("Saved")); Close(); } diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs index 8d592aee..a51884c5 100644 --- a/Netch/Forms/MainForm.cs +++ b/Netch/Forms/MainForm.cs @@ -71,11 +71,15 @@ namespace Netch.Forms // 计算 ComboBox绘制 目标宽度 RecordSize(); - LoadServers(); + // Bind Server + ServerComboBox.DataSource = Global.Settings.Server; + SelectLastServer(); ServerHelper.DelayTestHelper.UpdateInterval(); + // Load and Bind Mode ModeHelper.Load(); - LoadModes(); + ModeComboBox.DataSource = Global.Modes; + SelectLastMode(); // 加载翻译 TranslateControls(); @@ -207,7 +211,7 @@ namespace Netch.Forms #region Server - private void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e) + private async void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e) { var texts = Clipboard.GetText(); if (!string.IsNullOrWhiteSpace(texts)) @@ -216,12 +220,11 @@ namespace Netch.Forms Global.Settings.Server.AddRange(servers); NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count)); - LoadServers(); - Configuration.Save(); + await Configuration.SaveAsync(); } } - private void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e) + private async void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e) { if (sender == null) throw new ArgumentNullException(nameof(sender)); @@ -231,8 +234,7 @@ namespace Netch.Forms Hide(); util.Create(); - LoadServers(); - Configuration.Save(); + await Configuration.SaveAsync(); Show(); } @@ -262,7 +264,6 @@ namespace Netch.Forms { Hide(); new SubscribeForm().ShowDialog(); - LoadServers(); Show(); } @@ -291,8 +292,7 @@ namespace Netch.Forms { await Subscription.UpdateServersAsync(); - LoadServers(); - Configuration.Save(); + await Configuration.SaveAsync(); StatusText(i18N.Translate("Subscription updated")); } catch (Exception e) @@ -433,7 +433,7 @@ namespace Netch.Forms { if (exception is not MessageException) { - Log.Error(exception, "更新失败"); + Log.Error(exception, "更新未处理异常"); } NotifyTip(exception.Message, info: false); @@ -469,7 +469,7 @@ namespace Netch.Forms return; } - Configuration.Save(); + await Configuration.SaveAsync(); // 服务器、模式 需选择 if (ServerComboBox.SelectedItem is not Server server) @@ -541,7 +541,6 @@ namespace Netch.Forms { i18N.Load(Global.Settings.Language); TranslateControls(); - LoadModes(); LoadProfiles(); } @@ -558,13 +557,6 @@ namespace Netch.Forms #region Server - private void LoadServers() - { - ServerComboBox.Items.Clear(); - ServerComboBox.Items.AddRange(Global.Settings.Server.Cast().ToArray()); - SelectLastServer(); - } - private void SelectLastServer() { // 如果值合法,选中该位置 @@ -582,7 +574,7 @@ namespace Netch.Forms Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex; } - private void EditServerPictureBox_Click(object sender, EventArgs e) + private async void EditServerPictureBox_Click(object sender, EventArgs e) { // 当前ServerComboBox中至少有一项 if (!(ServerComboBox.SelectedItem is Server server)) @@ -596,8 +588,7 @@ namespace Netch.Forms Hide(); ServerHelper.GetUtilByTypeName(server.Type).Edit(server); - LoadServers(); - Configuration.Save(); + await Configuration.SaveAsync(); Show(); } @@ -670,21 +661,12 @@ namespace Netch.Forms } Global.Settings.Server.Remove(server); - LoadServers(); } #endregion #region Mode - public void LoadModes() - { - ModeComboBox.Items.Clear(); - ModeComboBox.Items.AddRange(Global.Modes.Cast().ToArray()); - ModeComboBox.Tag = null; - SelectLastMode(); - } - private void SelectLastMode() { // 如果值合法,选中该位置 @@ -1225,7 +1207,7 @@ namespace Netch.Forms Hide(); if (saveConfiguration) - Configuration.Save(); + await Configuration.SaveAsync(); foreach (var file in new[] { Constants.TempConfig, Constants.TempRouteFile }) if (File.Exists(file)) diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs index 1d65cfab..3a71aca5 100644 --- a/Netch/Forms/SettingForm.cs +++ b/Netch/Forms/SettingForm.cs @@ -244,7 +244,7 @@ namespace Netch.Forms Show(); } - private void ControlButton_Click(object sender, EventArgs e) + private async void ControlButton_Click(object sender, EventArgs e) { Utils.Utils.ComponentIterator(this, component => Utils.Utils.ChangeControlForeColor(component, Color.Black)); @@ -268,7 +268,7 @@ namespace Netch.Forms Utils.Utils.RegisterNetchStartupItem(); - Configuration.Save(); + await Configuration.SaveAsync(); MessageBoxX.Show(i18N.Translate("Saved")); Close(); } diff --git a/Netch/Forms/SubscribeForm.cs b/Netch/Forms/SubscribeForm.cs index 1edbe8fb..2109dedb 100644 --- a/Netch/Forms/SubscribeForm.cs +++ b/Netch/Forms/SubscribeForm.cs @@ -57,9 +57,9 @@ namespace Netch.Forms Global.Settings.SubscribeLink[index].Enable = SubscribeLinkListView.Items[index].Checked; } - private void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e) + private async void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e) { - Configuration.Save(); + await Configuration.SaveAsync(); } #endregion diff --git a/Netch/Netch.cs b/Netch/Netch.cs index ec21de9b..c121bb1d 100644 --- a/Netch/Netch.cs +++ b/Netch/Netch.cs @@ -44,7 +44,7 @@ namespace Netch Directory.CreateDirectory(item); // 加载配置 - Configuration.Load(); + Configuration.LoadAsync().Wait(); if (!SingleInstance.IsFirstInstance) { diff --git a/Netch/Services/Updater.cs b/Netch/Services/Updater.cs index 690ff41a..224a93b3 100644 --- a/Netch/Services/Updater.cs +++ b/Netch/Services/Updater.cs @@ -94,8 +94,6 @@ namespace Netch.Services #region Apply Update - private static readonly ImmutableArray KeepDirectories = new List { "data", "mode\\Custom", "logging" }.ToImmutableArray(); - private async Task ApplyUpdate() { var mainForm = Global.MainForm; @@ -105,17 +103,7 @@ namespace Netch.Services ModeHelper.SuspendWatcher = true; // Stop and Save await mainForm.Stop(); - Configuration.Save(); - - // Backup Configuration file - try - { - File.Copy(Configuration.SettingFileFullName, Configuration.SettingFileFullName + ".bak", true); - } - catch (Exception) - { - // ignored - } + await Configuration.SaveAsync(); #endregion @@ -123,13 +111,22 @@ namespace Netch.Services var extractPath = Path.Combine(_tempDirectory, "extract"); int exitCode; if ((exitCode = Extract(extractPath, true)) != 0) - throw new Exception(i18N.Translate($"7za exit with code {exitCode}")); + throw new MessageException(i18N.Translate($"7za unexpectedly exited. ({exitCode})")); + + var updateDirectory = Path.Combine(extractPath, "Netch"); + if (!Directory.Exists(updateDirectory)) + throw new MessageException(i18N.Translate("Update file top-level directory not exist")); + + var updateMainProgramFilePath = Path.Combine(updateDirectory, "Netch.exe"); + if (!File.Exists(updateMainProgramFilePath)) + throw new MessageException(i18N.Translate($"Update file main program not exist")); + // rename install directory files with .old suffix unless in keep folders MarkFilesOld(); // move {tempDirectory}\extract\Netch to install folder - MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _installDirectory); + MoveAllFilesOver(updateDirectory, _installDirectory); // release mutex, exit mainForm.Invoke(new Action(Netch.SingleInstance.Dispose)); @@ -139,33 +136,30 @@ namespace Netch.Services private void MarkFilesOld() { - // extend keepDirectories relative path to absolute path - var extendedKeepDirectories = KeepDirectories.Select(d => Path.Combine(_installDirectory, d)).ToImmutableArray(); + var keepDirs = new[] { "data", "mode\\Custom", "logging" }; + + var keepDirFullPath = keepDirs.Select(d => Path.Combine(_installDirectory, d)).ToImmutableList(); - // weed out keep files - List filesToDelete = new(); foreach (var file in Directory.GetFiles(_installDirectory, "*", SearchOption.AllDirectories)) { - if (extendedKeepDirectories.Any(p => file.StartsWith(p))) + // skip keep files + if (keepDirFullPath.Any(p => file.StartsWith(p))) continue; + // skip disable state files if (Path.GetFileName(file) is ModeHelper.DisableModeDirectoryFileName) continue; - filesToDelete.Add(file); - } - - // rename files - foreach (var file in filesToDelete) try { File.Move(file, file + ".old"); } - catch(Exception e) + catch (Exception e) { - Log.Error(e,"failed to rename file \"{File}\"",file); + Log.Error(e, "failed to rename file \"{File}\"", file); throw; } + } } private int Extract(string destDirName, bool overwrite) @@ -182,12 +176,9 @@ namespace Netch.Services if (overwrite) argument.Append(" -y"); - var process = Process.Start(new ProcessStartInfo + var process = Process.Start(new ProcessStartInfo(temp7za, argument.ToString()) { - UseShellExecute = false, - WindowStyle = ProcessWindowStyle.Hidden, - FileName = temp7za, - Arguments = argument.ToString() + UseShellExecute = false })!; process.WaitForExit(); diff --git a/Netch/Utils/Bandwidth.cs b/Netch/Utils/Bandwidth.cs index 39b79a70..54e3d1e8 100644 --- a/Netch/Utils/Bandwidth.cs +++ b/Netch/Utils/Bandwidth.cs @@ -5,6 +5,7 @@ using Netch.Models; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Serilog; @@ -79,7 +80,8 @@ namespace Netch.Utils var processList = instances.Select(instance => instance.Id).ToList(); - Log.Information("流量统计进程: {Processes}", $"{string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())}"); + Log.Information("流量统计进程: {Processes}", + $"{string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())}"); received = 0; @@ -118,7 +120,7 @@ namespace Netch.Utils while (Global.MainForm.State != State.Stopped) { - Task.Delay(1000).Wait(); + Thread.Sleep(1000); lock (counterLock) Global.MainForm.OnBandwidthUpdated(received); } diff --git a/Netch/Utils/Configuration.cs b/Netch/Utils/Configuration.cs index e77c78d7..e3daaef5 100644 --- a/Netch/Utils/Configuration.cs +++ b/Netch/Utils/Configuration.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading.Tasks; using Serilog; namespace Netch.Utils @@ -15,7 +16,13 @@ namespace Netch.Utils /// public static string DataDirectoryFullName => Path.Combine(Global.NetchDir, "data"); - public static string SettingFileFullName => $"{DataDirectoryFullName}\\settings.json"; + public static string FileFullName => Path.Combine(DataDirectoryFullName, FileName); + + private static string BackupFileFullName => Path.Combine(DataDirectoryFullName, BackupFileName); + + private const string FileName = "settings.json"; + + private const string BackupFileName = "settings.json.bak"; private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions; @@ -25,33 +32,44 @@ namespace Netch.Utils JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); } - /// - /// 加载配置 - /// - public static void Load() + public static async Task LoadAsync() { - if (File.Exists(SettingFileFullName)) + try { - try + if (!File.Exists(FileFullName)) { - using var fileStream = File.OpenRead(SettingFileFullName); - var settings = JsonSerializer.DeserializeAsync(fileStream, JsonSerializerOptions).Result!; - - CheckSetting(settings); - - Global.Settings = settings; - } - catch (Exception e) - { - Log.Error(e,"加载配置异常"); - Environment.Exit(-1); - Global.Settings = null!; + await SaveAsync(); + return; } + + if (await LoadAsyncCore(FileFullName)) + return; + + Log.Information("尝试加载备份配置文件 {FileName}", BackupFileFullName); + await LoadAsyncCore(BackupFileFullName); } - else + catch (Exception e) { - // 保存默认设置 - Save(); + Log.Error(e, "加载配置异常"); + Environment.Exit(-1); + } + } + + private static async ValueTask LoadAsyncCore(string filename) + { + try + { + await using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true); + var settings = (await JsonSerializer.DeserializeAsync(fs, JsonSerializerOptions))!; + + CheckSetting(settings); + Global.Settings = settings; + return true; + } + catch (Exception e) + { + Log.Error(e, @"从 {FileName} 加载配置异常", filename); + return false; } } @@ -70,13 +88,26 @@ namespace Netch.Utils /// /// 保存配置 /// - public static void Save() + public static async Task SaveAsync() { - if (!Directory.Exists(DataDirectoryFullName)) - Directory.CreateDirectory(DataDirectoryFullName); + try + { + if (!Directory.Exists(DataDirectoryFullName)) + Directory.CreateDirectory(DataDirectoryFullName); - using var fileStream = File.Create(SettingFileFullName); - JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).Wait(); + var tempFile = Path.Combine(DataDirectoryFullName, FileFullName + ".tmp"); + + await using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true)) + { + await JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions); + } + + File.Replace(tempFile, FileFullName, BackupFileFullName); + } + catch (Exception e) + { + Log.Error(e, "保存配置异常"); + } } } } \ No newline at end of file diff --git a/Netch/Utils/ModeHelper.cs b/Netch/Utils/ModeHelper.cs index 53481898..e3a2747a 100644 --- a/Netch/Utils/ModeHelper.cs +++ b/Netch/Utils/ModeHelper.cs @@ -42,7 +42,6 @@ namespace Netch.Utils return; Load(); - Global.MainForm.LoadModes(); } public static string GetRelativePath(string fullName) diff --git a/Netch/Utils/NetworkInterfaceUtils.cs b/Netch/Utils/NetworkInterfaceUtils.cs index b018e31e..6857b538 100644 --- a/Netch/Utils/NetworkInterfaceUtils.cs +++ b/Netch/Utils/NetworkInterfaceUtils.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics; using System.Linq; using System.Management; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Threading.Tasks; using Netch.Models; using Vanara.PInvoke; @@ -47,7 +49,11 @@ namespace Netch.Utils if (metric != null) arguments += $"metric={metric} "; - Utils.ProcessRunHiddenAsync("netsh", arguments).Wait(); + Process.Start(new ProcessStartInfo("netsh.exe", arguments) + { + UseShellExecute = false, + Verb = "runas" + })!.WaitForExit(); } } diff --git a/Netch/Utils/Utils.cs b/Netch/Utils/Utils.cs index f9dd0da5..2f438cc7 100644 --- a/Netch/Utils/Utils.cs +++ b/Netch/Utils/Utils.cs @@ -228,37 +228,6 @@ namespace Netch.Utils } } - public static async Task ProcessRunHiddenAsync(string fileName, string arguments = "", bool print = true) - { - var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - WindowStyle = ProcessWindowStyle.Hidden, - Verb = "runas", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; - - Log.Debug($"{fileName} {arguments}"); - - p.Start(); - var output = await p.StandardOutput.ReadToEndAsync(); - var error = await p.StandardError.ReadToEndAsync(); - if (print) - { - Console.Write(output); - Console.Write(error); - } - - await p.WaitForExitAsync(); - } - public static int SubnetToCidr(string value) { var subnet = IPAddress.Parse(value);