diff --git a/src/Common.ClientLib/Application/Essentials/Browser2.utils.cs b/src/Common.ClientLib/Application/Essentials/Browser2.utils.cs
index c50b28ef1..13366d8ff 100644
--- a/src/Common.ClientLib/Application/Essentials/Browser2.utils.cs
+++ b/src/Common.ClientLib/Application/Essentials/Browser2.utils.cs
@@ -30,13 +30,15 @@ namespace System.Application
Unknown = -2,
}
+ const string TAG = nameof(Browser2);
+
static void HandlerException(Exception e)
{
if (OnError == null)
{
try
{
- Toast.Show(e, nameof(Browser2));
+ e.LogAndShowT(TAG);
}
catch
{
diff --git a/src/Common.ClientLib/Application/Essentials/FilePicker2.cs b/src/Common.ClientLib/Application/Essentials/FilePicker2.cs
index c225e6229..76eb75844 100644
--- a/src/Common.ClientLib/Application/Essentials/FilePicker2.cs
+++ b/src/Common.ClientLib/Application/Essentials/FilePicker2.cs
@@ -57,6 +57,8 @@ namespace System.Application
}
}
+ const string TAG = nameof(FilePicker2);
+
///
/// 启动文件选择器以选择单个文件。
///
@@ -86,7 +88,7 @@ namespace System.Application
}
catch (Exception e)
{
- Toast.Show(e, nameof(FilePicker2));
+ e.LogAndShowT(TAG);
}
}
}
@@ -129,7 +131,7 @@ namespace System.Application
}
catch (Exception e)
{
- Toast.Show(e, nameof(FilePicker2));
+ e.LogAndShowT(TAG);
}
}
}
@@ -202,7 +204,7 @@ namespace System.Application
}
catch (Exception e)
{
- Toast.Show(e, nameof(FilePicker2));
+ e.LogAndShowT(TAG);
}
}
}
@@ -245,7 +247,7 @@ namespace System.Application
}
catch (Exception e)
{
- Toast.Show(e, nameof(FilePicker2));
+ e.LogAndShowT(TAG);
}
}
}
diff --git a/src/Common.ClientLib/Toast.cs b/src/Common.ClientLib/Toast.cs
index 8f231e234..e81715bbf 100644
--- a/src/Common.ClientLib/Toast.cs
+++ b/src/Common.ClientLib/Toast.cs
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using System.Application.Services;
+using System.Runtime.CompilerServices;
using System.Text;
namespace System
@@ -21,41 +22,42 @@ namespace System
toast.Show(text, duration);
}
+ static void ShowLong(string text) => Show(text, ToastLength.Long);
+
+#if DEBUG
+ [Obsolete("use e.LogAndShowT(..", true)]
+ public static void Show(Exception e,
+ string? tag = null, LogLevel level = LogLevel.Error,
+ string? msg = null, params object?[] args) => e.LogAndShowT(tag, level, "", msg, args);
+#endif
+
///
- /// 通过 显示 Toast 并纪录日志,传入 可不写日志
+ /// 通过 纪录日志并在 Toast 上显示,传入 可不写日志
///
///
+ ///
///
///
///
///
- public static void Show(Exception e, string? tag = null, LogLevel level = LogLevel.Error, string? msg = null, params string[] args)
- {
- if (string.IsNullOrWhiteSpace(tag)) tag = nameof(Toast);
- var has_msg = !string.IsNullOrWhiteSpace(msg);
- var has_args = args.Any_Nullable();
- if (level < LogLevel.None)
- {
- var logger = Log.CreateLogger(tag);
- if (has_msg)
- {
- if (has_args)
- {
- logger.Log(level, e, msg!, args);
- }
- else
- {
- logger.Log(level, e, msg!);
- }
- }
- else
- {
- logger.Log(level, e, null);
- }
- }
+ ///
+ public static void LogAndShowT(this Exception? e,
+ string? tag = null, LogLevel level = LogLevel.Error,
+ [CallerMemberName] string memberName = "",
+ string? msg = null, params object?[] args)
+ => ExceptionExtensions.LogAndShow(e,
+ ShowLong,
+ string.IsNullOrWhiteSpace(tag) ? nameof(Toast) : tag, level,
+ memberName,
+ msg, args);
- var text = ExceptionExtensions.GetAllMessageCore(e, has_msg, has_args, msg, args);
- Show(text, ToastLength.Long);
- }
+ ///
+ public static void LogAndShowT(this Exception? e,
+ ILogger logger, LogLevel level = LogLevel.Error,
+ [CallerMemberName] string memberName = "",
+ string? msg = null, params object?[] args) => ExceptionExtensions.LogAndShow(e,
+ ShowLong,
+ logger, level,
+ memberName, msg, args);
}
}
\ No newline at end of file
diff --git a/src/Common.CoreLib/Extensions/ExceptionExtensions.cs b/src/Common.CoreLib/Extensions/ExceptionExtensions.cs
index f60dd32b2..af25bced0 100644
--- a/src/Common.CoreLib/Extensions/ExceptionExtensions.cs
+++ b/src/Common.CoreLib/Extensions/ExceptionExtensions.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Text;
+using Microsoft.Extensions.Logging;
// ReSharper disable once CheckNamespace
namespace System
@@ -10,11 +11,20 @@ namespace System
{
static bool JavaInterop => OperatingSystem2.IsAndroid;
+ ///
+ /// 获取异常是否为已知类型,通常不为 的异常不需要纪录日志
+ ///
+ ///
+ ///
public static ExceptionKnownType GetKnownType(this Exception e)
{
- if (e is OperationCanceledException)
+ if (e is TaskCanceledException)
{
- return ExceptionKnownType.Canceled;
+ return ExceptionKnownType.TaskCanceled;
+ }
+ else if (e is OperationCanceledException)
+ {
+ return ExceptionKnownType.OperationCanceled;
}
if (JavaInterop)
{
@@ -42,34 +52,145 @@ namespace System
///
/// 是否为取消操作异常
///
- ///
+ ///
///
- [Obsolete("use GetKnownType")]
- public static bool IsCanceledException(this Exception e) => GetKnownType(e) == ExceptionKnownType.Canceled;
+ public static bool IsCanceledException(this ExceptionKnownType value)
+ => value == ExceptionKnownType.Canceled ||
+ value == ExceptionKnownType.OperationCanceled ||
+ value == ExceptionKnownType.TaskCanceled;
+
+ /////
+ ///// 是否为取消操作异常
+ /////
+ /////
+ /////
+ //[Obsolete("use GetKnownType", true)]
+ //public static bool IsCanceledException(this Exception e) => GetKnownType(e).IsCanceledException();
+
+ ///
+ /// 通过 纪录日志并在 UI 上显示,传入 可不写日志
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void LogAndShow(Exception? e,
+ Action? show,
+ string tag, LogLevel level = LogLevel.Error,
+ string memberName = "",
+ string? msg = null, params object?[] args) => LogAndShowCore(e, show, tag, logger: null, level, memberName, msg, args);
+
+ ///
+ public static void LogAndShow(Exception? e,
+ Action? show,
+ ILogger? logger, LogLevel level = LogLevel.Error,
+ string memberName = "",
+ string? msg = null, params object?[] args) => LogAndShowCore(e, show, tag: null, logger, level, memberName, msg, args);
+
+ static void LogAndShowCore(Exception? e,
+ Action? show,
+ string? tag = null, ILogger? logger = null, LogLevel level = LogLevel.Error,
+ string memberName = "",
+ string? msg = null, params object?[] args)
+ {
+ bool has_msg = !string.IsNullOrWhiteSpace(msg);
+ if (!has_msg)
+ {
+ if (!string.IsNullOrWhiteSpace(memberName))
+ {
+ msg = $"{memberName} Error";
+ has_msg = true;
+ }
+ }
+ var has_args = args.Any_Nullable();
+ var has_e = e != null;
+ if (has_e)
+ {
+ var knownType = e!.GetKnownType();
+ if (knownType != ExceptionKnownType.Unknown) level = LogLevel.None;
+ }
+ var has_log_level = level < LogLevel.None;
+ if (has_log_level)
+ {
+ if (logger == null && tag != null)
+ {
+ logger = Log.CreateLogger(tag);
+ }
+ if (logger != null)
+ {
+ if (has_args)
+ {
+ logger.Log(level, e, msg!, args);
+ }
+ else
+ {
+ logger.Log(level, e, msg!);
+ }
+ }
+ }
+ show?.Invoke(GetShowMsg());
+ string GetShowMsg()
+ {
+ if (has_e) return GetAllMessageCore(e!, has_msg, has_args, msg, args);
+ if (has_msg)
+ {
+ if (has_args)
+ {
+ return msg!.Format(args);
+ }
+ else
+ {
+ return msg!;
+ }
+ }
+ return "";
+ }
+ }
///
/// 获取异常中所有错误信息
///
/// 当前捕获的异常
+ ///
+ ///
/// 可选的消息,将写在第一行
/// 可选的消息参数
///
- public static string GetAllMessage(this Exception e, string? msg = null, params string[] args)
+ public static string GetAllMessage(this Exception e, string? msg = null, params object?[] args)
{
var has_msg = !string.IsNullOrWhiteSpace(msg);
var has_args = args.Any_Nullable();
return GetAllMessageCore(e, has_msg, has_args, msg, args);
}
- ///
- public static string GetAllMessageCore(Exception e, bool has_msg, bool has_args, string? msg = null, params string[] args)
+ ///
+ static string GetAllMessageCore(Exception e,
+ bool has_msg, bool has_args,
+ string? msg = null, params object?[] args)
{
StringBuilder sb = new();
+
if (has_msg)
{
if (has_args)
{
- sb.AppendFormat(msg, args);
+ try
+ {
+ sb.AppendFormat(msg, args);
+ }
+ catch
+ {
+ sb.Append(msg);
+ foreach (var item in args)
+ {
+ sb.Append(' ');
+ sb.Append(item);
+ }
+ }
sb.AppendLine();
}
else
@@ -77,27 +198,17 @@ namespace System
sb.AppendLine(msg!);
}
}
- var e_str = e.ToString();
- sb.AppendLine(e_str);
- var stackTrace = e.StackTrace;
- if (!string.IsNullOrWhiteSpace(stackTrace) &&
- !e_str.Contains(stackTrace))
- {
- sb.AppendLine(stackTrace);
- }
var exception = e;
ushort i = 0;
while (exception != null && i++ < byte.MaxValue)
{
var exception_message = exception.Message;
- if (!string.IsNullOrWhiteSpace(exception_message) &&
- !e_str.Contains(exception_message))
+ if (!string.IsNullOrWhiteSpace(exception_message))
{
sb.AppendLine(exception_message);
}
exception = exception.InnerException;
-
}
var text = sb.ToString().Trim();
@@ -105,6 +216,9 @@ namespace System
}
}
+ ///
+ /// 异常已知类型,通常不为 的异常不需要纪录日志
+ ///
public enum ExceptionKnownType : byte
{
Unknown,
@@ -118,5 +232,11 @@ namespace System
/// 证书时间验证错误异常,通常为本地时间不正确导致 SSL 握手失败或服务端证书失效
///
CertificateNotYetValid,
+
+ ///
+ OperationCanceled,
+
+ ///
+ TaskCanceled,
}
}
\ No newline at end of file
diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/CommunityProxyPage.axaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/CommunityProxyPage.axaml
index 83990b542..7b564b87a 100644
--- a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/CommunityProxyPage.axaml
+++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/CommunityProxyPage.axaml
@@ -131,6 +131,7 @@
+
@@ -138,6 +139,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ST.Client.Desktop.Linux/Properties/SR.Designer.cs b/src/ST.Client.Desktop.Linux/Properties/SR.Designer.cs
new file mode 100644
index 000000000..bd851ee26
--- /dev/null
+++ b/src/ST.Client.Desktop.Linux/Properties/SR.Designer.cs
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace System.Application.Properties {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class SR {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal SR() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Application.Properties.SR", typeof(SR).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 127.0.0.1 localhost
+ ///127.0.1.1 ${USERNAME}
+ ///
+ ///# The following lines are desirable for IPv6 capable hosts
+ ///::1 ip6-localhost ip6-loopback
+ ///fe00::0 ip6-localnet
+ ///ff00::0 ip6-mcastprefix
+ ///ff02::1 ip6-allnodes
+ ///ff02::2 ip6-allrouters
+ /// 的本地化字符串。
+ ///
+ internal static string hosts {
+ get {
+ return ResourceManager.GetString("hosts", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/ST.Client.Desktop.Linux/Properties/SR.resx b/src/ST.Client.Desktop.Linux/Properties/SR.resx
new file mode 100644
index 000000000..dc27701e1
--- /dev/null
+++ b/src/ST.Client.Desktop.Linux/Properties/SR.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\hosts.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312
+
+
\ No newline at end of file
diff --git a/src/ST.Client.Desktop.Linux/Resources/hosts.txt b/src/ST.Client.Desktop.Linux/Resources/hosts.txt
new file mode 100644
index 000000000..9b6cbe4b9
--- /dev/null
+++ b/src/ST.Client.Desktop.Linux/Resources/hosts.txt
@@ -0,0 +1,9 @@
+127.0.0.1 localhost
+127.0.1.1 ${USERNAME}
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
diff --git a/src/ST.Client.Desktop.Linux/ST.Client.Linux.csproj b/src/ST.Client.Desktop.Linux/ST.Client.Linux.csproj
index 04e8489b9..8d255291a 100644
--- a/src/ST.Client.Desktop.Linux/ST.Client.Linux.csproj
+++ b/src/ST.Client.Desktop.Linux/ST.Client.Linux.csproj
@@ -51,4 +51,19 @@
+
+
+
+ True
+ True
+ SR.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ SR.Designer.cs
+
+
\ No newline at end of file
diff --git a/src/ST.Client.Desktop.Linux/Services/Implementation/LinuxPlatformServiceImpl.cs b/src/ST.Client.Desktop.Linux/Services/Implementation/LinuxPlatformServiceImpl.cs
index 5e525f66d..d8da0cf85 100644
--- a/src/ST.Client.Desktop.Linux/Services/Implementation/LinuxPlatformServiceImpl.cs
+++ b/src/ST.Client.Desktop.Linux/Services/Implementation/LinuxPlatformServiceImpl.cs
@@ -1,4 +1,5 @@
using System.Application.Models;
+using System.Application.Properties;
using System.Application.UI.Resx;
using System.Application.UI.ViewModels;
using System.Diagnostics;
@@ -208,8 +209,7 @@ namespace System.Application.Services.Implementation
}
catch (Exception e)
{
- Log.Error("Shell Error", e, "Run Shell Error");
- Toast.Show(e);
+ e.LogAndShowT(TAG);
}
return string.Empty;
}
@@ -225,7 +225,7 @@ namespace System.Application.Services.Implementation
};
var pwd = await TextBoxWindowViewModel.ShowDialogAsync(vm);
if (!string.IsNullOrWhiteSpace(pwd))
- {
+ {
if (!string.IsNullOrWhiteSpace(RunShell($"echo \"{pwd}\" | sudo -S sh -c \"sudo -n true\"")))
{
SystemUserPassword = pwd;
@@ -246,5 +246,7 @@ namespace System.Application.Services.Implementation
}
}
}
+
+ public string DefaultHostsContent => SR.hosts.Replace("${USERNAME}", Environment.UserName);
}
}
\ No newline at end of file
diff --git a/src/ST.Client.Desktop.Mac/Internals/UnixHelper.RunShellAsync.cs b/src/ST.Client.Desktop.Mac/Internals/UnixHelper.RunShellAsync.cs
index 2235d2bf2..4ecf28728 100644
--- a/src/ST.Client.Desktop.Mac/Internals/UnixHelper.RunShellAsync.cs
+++ b/src/ST.Client.Desktop.Mac/Internals/UnixHelper.RunShellAsync.cs
@@ -9,6 +9,8 @@ using System.Threading.Tasks;
static partial class UnixHelper
{
+ const string TAG = nameof(UnixHelper);
+
static readonly Lazy _bin_bash = new(() => string.Format("{0}bin{0}bash", Path.DirectorySeparatorChar));
///
/// /bin/bash
@@ -98,7 +100,7 @@ static partial class UnixHelper
try
{
using var p = new Process();
- p.StartInfo.FileName = UnixHelper.BinBash;
+ p.StartInfo.FileName = BinBash;
p.StartInfo.Arguments = "";
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
@@ -115,8 +117,7 @@ static partial class UnixHelper
}
catch (Exception e)
{
- Log.Error("Shell Error", e, "Run Shell Error");
- Toast.Show(e);
+ e.LogAndShowT(TAG);
}
return string.Empty;
}
diff --git a/src/ST.Client.Desktop.Mac/Services/Implementation/MacPlatformServiceImpl.cs b/src/ST.Client.Desktop.Mac/Services/Implementation/MacPlatformServiceImpl.cs
index ef08935ad..2bd745012 100644
--- a/src/ST.Client.Desktop.Mac/Services/Implementation/MacPlatformServiceImpl.cs
+++ b/src/ST.Client.Desktop.Mac/Services/Implementation/MacPlatformServiceImpl.cs
@@ -292,8 +292,7 @@ namespace System.Application.Services.Implementation
}
catch (Exception e)
{
- Log.Error("OSAScript Error", e, "Run OSAScript Error");
- Toast.Show(e);
+ e.LogAndShowT(TAG);
}
return string.Empty;
}
diff --git a/src/ST.Client.Desktop.Windows/Properties/SR.Designer.cs b/src/ST.Client.Desktop.Windows/Properties/SR.Designer.cs
index ea0fc2982..d869de8ca 100644
--- a/src/ST.Client.Desktop.Windows/Properties/SR.Designer.cs
+++ b/src/ST.Client.Desktop.Windows/Properties/SR.Designer.cs
@@ -60,6 +60,26 @@ namespace System.Application.Properties {
}
}
+ ///
+ /// 查找类似 # Copyright (c) 1993-2009 Microsoft Corp.
+ ///#
+ ///# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
+ ///#
+ ///# This file contains the mappings of IP addresses to host names. Each
+ ///# entry should be kept on an individual line. The IP address should
+ ///# be placed in the first column followed by the corresponding host name.
+ ///# The IP address and the host name should be separated by at least one
+ ///# space.
+ ///#
+ ///# Additionally, comments (such as these) may be inserted on individual
+ ///# lines or following th [字符串的其余部分被截断]"; 的本地化字符串。
+ ///
+ internal static string hosts {
+ get {
+ return ResourceManager.GetString("hosts", resourceCulture);
+ }
+ }
+
///
/// 查找类似 @echo off
///color a
diff --git a/src/ST.Client.Desktop.Windows/Properties/SR.resx b/src/ST.Client.Desktop.Windows/Properties/SR.resx
index 159a485f9..aee9373e9 100644
--- a/src/ST.Client.Desktop.Windows/Properties/SR.resx
+++ b/src/ST.Client.Desktop.Windows/Properties/SR.resx
@@ -112,11 +112,15 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\hosts.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312
+
@echo off
color a
diff --git a/src/ST.Client.Desktop.Windows/Resources/hosts.txt b/src/ST.Client.Desktop.Windows/Resources/hosts.txt
new file mode 100644
index 000000000..63847a2c9
--- /dev/null
+++ b/src/ST.Client.Desktop.Windows/Resources/hosts.txt
@@ -0,0 +1,21 @@
+# Copyright (c) 1993-2009 Microsoft Corp.
+#
+# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
+#
+# This file contains the mappings of IP addresses to host names. Each
+# entry should be kept on an individual line. The IP address should
+# be placed in the first column followed by the corresponding host name.
+# The IP address and the host name should be separated by at least one
+# space.
+#
+# Additionally, comments (such as these) may be inserted on individual
+# lines or following the machine name denoted by a '#' symbol.
+#
+# For example:
+#
+# 102.54.94.97 rhino.acme.com # source server
+# 38.25.63.10 x.acme.com # x client host
+
+# localhost name resolution is handled within DNS itself.
+# 127.0.0.1 localhost
+# ::1 localhost
diff --git a/src/ST.Client.Desktop.Windows/Services/Implementation/NativeWindowApiServiceImpl.cs b/src/ST.Client.Desktop.Windows/Services/Implementation/NativeWindowApiServiceImpl.cs
index 6a0f4a2b1..768fce19c 100644
--- a/src/ST.Client.Desktop.Windows/Services/Implementation/NativeWindowApiServiceImpl.cs
+++ b/src/ST.Client.Desktop.Windows/Services/Implementation/NativeWindowApiServiceImpl.cs
@@ -11,6 +11,8 @@ namespace System.Application.Services.Implementation
[SupportedOSPlatform("Windows7.0")]
internal sealed class NativeWindowApiServiceImpl : INativeWindowApiService
{
+ const string TAG = "NativeWindowApiS";
+
///
/// 拖拽指针获取目标窗口
///
@@ -62,7 +64,7 @@ namespace System.Application.Services.Implementation
}
catch (Exception e)
{
- Toast.Show(e, nameof(NativeWindowApiServiceImpl));
+ e.LogAndShowT(TAG);
}
}
MouseHook.SetSystemCursor(MouseHook.LoadCursor(IntPtr.Zero, MouseHook.OCR_CROSS), MouseHook.OCR_NORMAL);
diff --git a/src/ST.Client.Desktop.Windows/Services/Implementation/WindowsPlatformServiceImpl.cs b/src/ST.Client.Desktop.Windows/Services/Implementation/WindowsPlatformServiceImpl.cs
index e5e696b16..99cc199bb 100644
--- a/src/ST.Client.Desktop.Windows/Services/Implementation/WindowsPlatformServiceImpl.cs
+++ b/src/ST.Client.Desktop.Windows/Services/Implementation/WindowsPlatformServiceImpl.cs
@@ -18,6 +18,7 @@ using System.Runtime.Versioning;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Windows.ApplicationModel;
+using System.Application.Properties;
namespace System.Application.Services.Implementation
{
@@ -54,6 +55,8 @@ namespace System.Application.Services.Implementation
public string HostsFilePath => mHostsFilePath.Value;
+ public string DefaultHostsContent => SR.hosts;
+
static string GetFolderPath(Environment.SpecialFolder folder)
{
switch (folder)
diff --git a/src/ST.Client/MessageBox.cs b/src/ST.Client/MessageBox.cs
index a4c211cb8..52390b1bb 100644
--- a/src/ST.Client/MessageBox.cs
+++ b/src/ST.Client/MessageBox.cs
@@ -88,6 +88,8 @@ namespace System.Application
/// 捐助提示
///
Donate = 2,
+
+ ResetHostsFile,
}
///
@@ -155,12 +157,12 @@ namespace System.Application
await ShowAsync(messageBoxText, caption, button, icon);
}
- ///
- public static Task ShowAsync(Exception exception, string caption = default_caption, Button button = default_button, Image icon = default)
- {
- var messageBoxText = exception.GetAllMessage();
- return ShowAsync(messageBoxText, caption, button, icon);
- }
+ /////
+ //public static Task ShowAsync(Exception exception, string caption = default_caption, Button button = default_button, Image icon = default)
+ //{
+ // var messageBoxText = exception.GetAllMessage();
+ // return ShowAsync(messageBoxText, caption, button, icon);
+ //}
///
public static void Show(Exception exception, string caption = default_caption, Button button = default_button, Image icon = default)
diff --git a/src/ST.Client/Services/IHostsFileService.cs b/src/ST.Client/Services/IHostsFileService.cs
index f9ac5474c..dd0e77f41 100644
--- a/src/ST.Client/Services/IHostsFileService.cs
+++ b/src/ST.Client/Services/IHostsFileService.cs
@@ -16,6 +16,16 @@ namespace System.Application.Services
///
void OpenFile();
+ ///
+ /// 打开 hosts 所在文件夹
+ ///
+ void OpenFileDir();
+
+ ///
+ /// 重置 hosts 文件
+ ///
+ void ResetFile();
+
///
/// 读取 hosts 文件
///
diff --git a/src/ST.Client/Services/IPlatformService.cs b/src/ST.Client/Services/IPlatformService.cs
index 95bc92625..88d869c74 100644
--- a/src/ST.Client/Services/IPlatformService.cs
+++ b/src/ST.Client/Services/IPlatformService.cs
@@ -136,6 +136,11 @@ namespace System.Application.Services
///
string HostsFilePath => string.Format("{0}etc{0}hosts", Path.DirectorySeparatorChar);
+ ///
+ /// 默认 hosts 文件内容
+ ///
+ string DefaultHostsContent => string.Empty;
+
///
/// 使用资源管理器打开某个路径
///
diff --git a/src/ST.Client/Services/Implementation/ArchiSteamFarmServiceImpl.cs b/src/ST.Client/Services/Implementation/ArchiSteamFarmServiceImpl.cs
index fed024e3f..961d5bcd4 100644
--- a/src/ST.Client/Services/Implementation/ArchiSteamFarmServiceImpl.cs
+++ b/src/ST.Client/Services/Implementation/ArchiSteamFarmServiceImpl.cs
@@ -20,6 +20,8 @@ namespace System.Application.Services.Implementation
{
public partial class ArchiSteamFarmServiceImpl : ReactiveObject, IArchiSteamFarmService
{
+ const string TAG = "ArchiSteamFarmS";
+
public event Action? OnConsoleWirteLine;
public TaskCompletionSource? ReadLineTask { get; set; }
@@ -80,9 +82,9 @@ namespace System.Application.Services.Implementation
}
}
}
- catch (Exception ex)
+ catch (Exception e)
{
- Toast.Show(ex, nameof(ArchiSteamFarmServiceImpl));
+ e.LogAndShowT(TAG);
await Stop().ConfigureAwait(false);
}
}
diff --git a/src/ST.Client/Services/Implementation/HostsFileServiceImpl.cs b/src/ST.Client/Services/Implementation/HostsFileServiceImpl.cs
index 7f47337f5..c0c2e887e 100644
--- a/src/ST.Client/Services/Implementation/HostsFileServiceImpl.cs
+++ b/src/ST.Client/Services/Implementation/HostsFileServiceImpl.cs
@@ -74,6 +74,15 @@ namespace System.Application.Services.Implementation
///
const long MaxFileLength = 52428800;
+ void WriteDefaultHostsContent(FileStream stream)
+ {
+ var encoding = GetEncoding();
+ using StreamWriter sw = new(stream, encoding);
+ sw.Write(s.DefaultHostsContent);
+ sw.Flush();
+ stream.SetLength(stream.Position);
+ }
+
///
/// 尝试开始操作前
///
@@ -98,7 +107,7 @@ namespace System.Application.Services.Implementation
{
try
{
- fileInfo.Create().Dispose();
+ WriteDefaultHostsContent(fileInfo.Create());
return true;
}
catch (Exception ex)
@@ -317,7 +326,7 @@ namespace System.Application.Services.Implementation
var removeLen = last_line_value.Length + Environment.NewLine.Length;
stringBuilder.Remove(stringBuilder.Length - removeLen, removeLen);
}
- if (!markLength.Add(mark)) throw new Exception($"hosts file mark duplicate, value: {mark}");
+ if (!markLength.Add(mark)) throw new HandleHostsFileException(AppResources.CommunityFix_Hosts_MarkDuplicate_.Format(mark));
return null;
});
if (is_effective_value_v2 != HandleLineResult.Duplicate)
@@ -461,7 +470,11 @@ namespace System.Application.Services.Implementation
Log.Error(TAG, ex, "UpdateHosts catch.");
result.ResultType = OperationResultType.Error;
result.AppendData = ex;
- if (ex is UnauthorizedAccessException || ex is SecurityException)
+ if (ex is HandleHostsFileException)
+ {
+ result.Message = ex.Message;
+ }
+ else if (ex is UnauthorizedAccessException || ex is SecurityException)
{
result.Message = AppResources.FileUnauthorized;
}
@@ -481,6 +494,30 @@ namespace System.Application.Services.Implementation
public void OpenFile() => s.OpenFileByTextReader(s.HostsFilePath);
+ public void OpenFileDir() => s.OpenFolder(s.HostsFilePath);
+
+ public async void ResetFile()
+ {
+ var r = await MessageBox.ShowAsync(
+ AppResources.CommunityFix_ResetHostsFileTip,
+ AppResources.Warning,
+ button: MessageBox.Button.OKCancel,
+ rememberChooseKey: MessageBox.DontPromptType.ResetHostsFile);
+ if (r.IsOK())
+ {
+ try
+ {
+ WriteDefaultHostsContent(new FileStream(s.HostsFilePath, FileMode.OpenOrCreate));
+ Toast.Show(AppResources.CommunityFix_ResetHostsFileOk);
+ }
+ catch (Exception e)
+ {
+ Log.Error(TAG, e, nameof(ResetFile));
+ Toast.Show(AppResources.CommunityFix_ResetHostsFileCatchTip_.Format(e.Message));
+ }
+ }
+ }
+
///
/// 获取 hosts 文件编码
///
@@ -695,5 +732,13 @@ namespace System.Application.Services.Implementation
mOccupyHostsFileStream = new FileStream(s.HostsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
#endif
+
+ sealed class HandleHostsFileException : Exception
+ {
+ public HandleHostsFileException(string message) : base(message)
+ {
+
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs b/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs
index f8244ddf1..9e5ddc57e 100644
--- a/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs
+++ b/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs
@@ -428,8 +428,7 @@ namespace System.Application.Services.Implementation
#if DEBUG
catch (Exception e)
{
- Toast.Show("TrustRootCertificate Error" + Environment.NewLine + e.GetAllMessage());
- Log.Error(TAG, e, "CertificateManager.TrustRootCertificate catch.");
+ e.LogAndShowT(TAG, msg: "TrustRootCertificate Error");
}
#else
catch { }
@@ -442,8 +441,7 @@ namespace System.Application.Services.Implementation
#if DEBUG
catch (Exception e)
{
- Toast.Show("EnsureRootCertificate Error" + Environment.NewLine + e.GetAllMessage());
- Log.Error(TAG, e, "CertificateManager.EnsureRootCertificate catch.");
+ e.LogAndShowT(TAG, msg: "EnsureRootCertificate Error");
}
#else
catch { }
@@ -743,7 +741,7 @@ namespace System.Application.Services.Implementation
}
catch (Exception ex)
{
- Toast.Show(ex, nameof(HttpProxyServiceImpl), msg: nameof(StopProxy));
+ ex.LogAndShowT(TAG);
}
}
diff --git a/src/ST.Client/Services/Mvvm/AuthService.cs b/src/ST.Client/Services/Mvvm/AuthService.cs
index 34ec22fee..bcb7b52c4 100644
--- a/src/ST.Client/Services/Mvvm/AuthService.cs
+++ b/src/ST.Client/Services/Mvvm/AuthService.cs
@@ -24,33 +24,40 @@ namespace System.Application.Services
{
public sealed class AuthService : ReactiveObject
{
+ const string TAG = "AuthS";
+
static AuthService? mCurrent;
public static AuthService Current => mCurrent ?? new();
+ static Stream? GetQrCodeStream(IEnumerable datas)
+ {
+ var dtos = datas.Select(x => x.ToLightweightExportDTO()).ToArray();
+ var bytes = Serializable.SMP(dtos);
+#if DEBUG
+ var bytes_compress_gzip = bytes.CompressByteArray();
+#endif
+ var bytes_compress_br = bytes.CompressByteArrayByBrotli();
+#if DEBUG
+ Toast.Show($"bytesLength, source: {bytes.Length}, gzip: {bytes_compress_gzip.Length}, br: {bytes_compress_br.Length}");
+#endif
+ (var result, var stream, var e) = QRCodeHelper.Create(bytes_compress_br);
+ switch (result)
+ {
+ case QRCodeHelper.QRCodeCreateResult.DataTooLong:
+ Toast.Show(AppResources.AuthLocal_ExportToQRCodeTooLongErrorTip);
+ break;
+ case QRCodeHelper.QRCodeCreateResult.Exception:
+ e?.LogAndShowT(TAG);
+ break;
+ }
+ return stream;
+ }
+
public static async Task GetQrCodeStreamAsync(IEnumerable datas)
{
var qrCode = await Task.Run(() =>
{
- var dtos = datas.Select(x => x.ToLightweightExportDTO()).ToArray();
- var bytes = Serializable.SMP(dtos);
-#if DEBUG
- var bytes_compress_gzip = bytes.CompressByteArray();
-#endif
- var bytes_compress_br = bytes.CompressByteArrayByBrotli();
-#if DEBUG
- Toast.Show($"bytesLength, source: {bytes.Length}, gzip: {bytes_compress_gzip.Length}, br: {bytes_compress_br.Length}");
-#endif
- (var result, var stream, var e) = QRCodeHelper.Create(bytes_compress_br);
- switch (result)
- {
- case QRCodeHelper.QRCodeCreateResult.DataTooLong:
- Toast.Show(AppResources.AuthLocal_ExportToQRCodeTooLongErrorTip);
- break;
- case QRCodeHelper.QRCodeCreateResult.Exception:
- Toast.Show(e!, nameof(AuthService), msg: nameof(GetQrCodeStreamAsync));
- break;
- }
- return stream;
+ return GetQrCodeStream(datas);
});
return qrCode;
}
@@ -765,9 +772,9 @@ namespace System.Application.Services
await fileWriteStream.FlushAsync();
await fileWriteStream.DisposeAsync();
}
- catch (Exception ex)
+ catch (Exception e)
{
- Toast.Show(ex, nameof(AuthService), msg: nameof(ExportAuthenticators));
+ e.LogAndShowT(TAG);
}
}
diff --git a/src/ST.Client/UI/Resx/AppResources.Designer.cs b/src/ST.Client/UI/Resx/AppResources.Designer.cs
index 54038573a..25ab3dee2 100644
--- a/src/ST.Client/UI/Resx/AppResources.Designer.cs
+++ b/src/ST.Client/UI/Resx/AppResources.Designer.cs
@@ -1202,6 +1202,15 @@ namespace System.Application.UI.Resx {
}
}
+ ///
+ /// 查找类似 Mark 出现多个 [{0}],请尝试重置 Hosts 文件或编辑 Hosts 文件并删除多余的 Mark 后再试一次 的本地化字符串。
+ ///
+ public static string CommunityFix_Hosts_MarkDuplicate_ {
+ get {
+ return ResourceManager.GetString("CommunityFix_Hosts_MarkDuplicate_", resourceCulture);
+ }
+ }
+
///
/// 查找类似 启用 VPN 模式 的本地化字符串。
///
@@ -1256,6 +1265,15 @@ namespace System.Application.UI.Resx {
}
}
+ ///
+ /// 查找类似 打开 Hosts 所在文件夹 的本地化字符串。
+ ///
+ public static string CommunityFix_OpenHostsDir {
+ get {
+ return ResourceManager.GetString("CommunityFix_OpenHostsDir", resourceCulture);
+ }
+ }
+
///
/// 查找类似 加速模式 的本地化字符串。
///
@@ -1337,6 +1355,42 @@ namespace System.Application.UI.Resx {
}
}
+ ///
+ /// 查找类似 重置 Hosts 文件 的本地化字符串。
+ ///
+ public static string CommunityFix_ResetHostsFile {
+ get {
+ return ResourceManager.GetString("CommunityFix_ResetHostsFile", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 重置 Hosts 文件失败,错误:{0} 的本地化字符串。
+ ///
+ public static string CommunityFix_ResetHostsFileCatchTip_ {
+ get {
+ return ResourceManager.GetString("CommunityFix_ResetHostsFileCatchTip_", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 重置 Hosts 文件成功 的本地化字符串。
+ ///
+ public static string CommunityFix_ResetHostsFileOk {
+ get {
+ return ResourceManager.GetString("CommunityFix_ResetHostsFileOk", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 重置 Hosts 文件将删除所有数据并还原当前操作系统默认值,建议先编辑 Hosts 文件查看是否有需要的数据先进行备份后再执行此操作,是否确认重置? 的本地化字符串。
+ ///
+ public static string CommunityFix_ResetHostsFileTip {
+ get {
+ return ResourceManager.GetString("CommunityFix_ResetHostsFileTip", resourceCulture);
+ }
+ }
+
///
/// 查找类似 管理脚本 的本地化字符串。
///
diff --git a/src/ST.Client/UI/Resx/AppResources.resx b/src/ST.Client/UI/Resx/AppResources.resx
index 2a868f57f..0b237c496 100644
--- a/src/ST.Client/UI/Resx/AppResources.resx
+++ b/src/ST.Client/UI/Resx/AppResources.resx
@@ -2033,4 +2033,22 @@ AAAAA-BBBBB-CCCC3
启用 VPN 模式
+
+ 打开 Hosts 所在文件夹
+
+
+ 重置 Hosts 文件
+
+
+ 重置 Hosts 文件失败,错误:{0}
+
+
+ 重置 Hosts 文件将删除所有数据并还原当前操作系统默认值,建议先编辑 Hosts 文件查看是否有需要的数据先进行备份后再执行此操作,是否确认重置?
+
+
+ Mark 出现多个 [{0}],请尝试重置 Hosts 文件或编辑 Hosts 文件并删除多余的 Mark 后再试一次
+
+
+ 重置 Hosts 文件成功
+
\ No newline at end of file
diff --git a/src/ST.Client/UI/ViewModels/Pages/CommunityProxyPageViewModel.cs b/src/ST.Client/UI/ViewModels/Pages/CommunityProxyPageViewModel.cs
index a30ead16f..9054999ce 100644
--- a/src/ST.Client/UI/ViewModels/Pages/CommunityProxyPageViewModel.cs
+++ b/src/ST.Client/UI/ViewModels/Pages/CommunityProxyPageViewModel.cs
@@ -30,6 +30,10 @@ namespace System.Application.UI.ViewModels
public ReactiveCommand? EditHostsFileCommand { get; }
+ public ReactiveCommand? OpenHostsDirCommand { get; }
+
+ public ReactiveCommand? ResetHostsFileCommand { get; }
+
public ReactiveCommand? NetworkFixCommand { get; }
//public ReactiveCommand AutoRunProxyCommand { get; }
@@ -57,6 +61,8 @@ namespace System.Application.UI.ViewModels
SetupCertificateCommand = ReactiveCommand.Create(SetupCertificate_OnClick);
DeleteCertificateCommand = ReactiveCommand.Create(DeleteCertificate_OnClick);
EditHostsFileCommand = ReactiveCommand.Create(hostsFileService.OpenFile);
+ OpenHostsDirCommand = ReactiveCommand.Create(hostsFileService.OpenFileDir);
+ ResetHostsFileCommand = ReactiveCommand.Create(hostsFileService.ResetFile);
NetworkFixCommand = ReactiveCommand.Create(ProxyService.Current.FixNetwork);
TrustCerCommand = ReactiveCommand.Create(TrustCer_OnClick);
OpenCertificateDirCommand = ReactiveCommand.Create(() =>
diff --git a/src/ST.Client/UI/ViewModels/Pages/GameListPageViewModel.cs b/src/ST.Client/UI/ViewModels/Pages/GameListPageViewModel.cs
index 472b21d67..9f19c15a1 100644
--- a/src/ST.Client/UI/ViewModels/Pages/GameListPageViewModel.cs
+++ b/src/ST.Client/UI/ViewModels/Pages/GameListPageViewModel.cs
@@ -16,6 +16,8 @@ namespace System.Application.UI.ViewModels
{
public partial class GameListPageViewModel
{
+ const string TAG = "GameListPageVM";
+
readonly Dictionary dictPinYinArray = new();
Func PredicateName(string? text)
{
@@ -301,9 +303,10 @@ namespace System.Application.UI.ViewModels
}
catch (Exception e)
{
- Toast.Show(e, nameof(GameListPageViewModel));
+ e.LogAndShowT(TAG);
}
}
+
public void AddAFKAppListFunc(SteamApp app)
{
try
@@ -321,9 +324,10 @@ namespace System.Application.UI.ViewModels
}
catch (Exception e)
{
- Toast.Show(e, nameof(GameListPageViewModel));
+ e.LogAndShowT(TAG);
}
}
+
public void AddHideAppList(SteamApp app)
{
try
@@ -336,9 +340,10 @@ namespace System.Application.UI.ViewModels
}
catch (Exception e)
{
- Toast.Show(e, nameof(GameListPageViewModel));
+ e.LogAndShowT(TAG);
}
}
+
public async void UnlockAchievement_Click(SteamApp app)
{
if (!ISteamService.Instance.IsRunningSteamProcess)
diff --git a/src/ST.Client/UI/ViewModels/Windows/LocalAuthPage/AuthTradeWindowViewModel.cs b/src/ST.Client/UI/ViewModels/Windows/LocalAuthPage/AuthTradeWindowViewModel.cs
index ffd3bf822..939d9317c 100644
--- a/src/ST.Client/UI/ViewModels/Windows/LocalAuthPage/AuthTradeWindowViewModel.cs
+++ b/src/ST.Client/UI/ViewModels/Windows/LocalAuthPage/AuthTradeWindowViewModel.cs
@@ -22,6 +22,8 @@ namespace System.Application.UI.ViewModels
{
public partial class AuthTradeWindowViewModel : MyAuthenticatorWindowViewModel
{
+ const string TAG = "AuthTradeWindowVM";
+
public AuthTradeWindowViewModel() : base()
{
}
@@ -547,12 +549,12 @@ namespace System.Application.UI.ViewModels
}
catch (WinAuthInvalidTradesResponseException ex)
{
- Toast.Show(ex, nameof(AuthTradeWindowViewModel), msg: nameof(AcceptTrade));
+ ex.LogAndShowT(TAG);
return false;
}
catch (ApplicationException ex)
{
- Toast.Show(ex, nameof(AuthTradeWindowViewModel), msg: nameof(AcceptTrade));
+ ex.LogAndShowT(TAG);
return false;
}
}
@@ -586,12 +588,12 @@ namespace System.Application.UI.ViewModels
}
catch (WinAuthInvalidTradesResponseException ex)
{
- Toast.Show(ex, nameof(AuthTradeWindowViewModel), msg: nameof(RejectTrade));
+ ex.LogAndShowT(TAG);
return false;
}
catch (ApplicationException ex)
{
- Toast.Show(ex, nameof(AuthTradeWindowViewModel), msg: nameof(RejectTrade));
+ ex.LogAndShowT(TAG);
return false;
}
}
diff --git a/src/ST.Services.CloudService/Services/CloudService/ApiConnection.cs b/src/ST.Services.CloudService/Services/CloudService/ApiConnection.cs
index d291f0bbe..ae312835c 100644
--- a/src/ST.Services.CloudService/Services/CloudService/ApiConnection.cs
+++ b/src/ST.Services.CloudService/Services/CloudService/ApiConnection.cs
@@ -66,6 +66,10 @@ namespace System.Application.Services.CloudService
{
case ExceptionKnownType.Canceled:
return (ApiResponseCode.Canceled, null);
+ case ExceptionKnownType.OperationCanceled:
+ return (ApiResponseCode.OperationCanceled, null);
+ case ExceptionKnownType.TaskCanceled:
+ return (ApiResponseCode.TaskCanceled, null);
case ExceptionKnownType.CertificateNotYetValid:
return (ApiResponseCode.CertificateNotYetValid, null);
}