diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml
index a73bbbae..d6789e48 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml
@@ -4,6 +4,7 @@
https://hut.ao
https://hut.ao/features/mhy-account-switch.html
https://translate.hut.ao
+ https://hut.ao/zh/advanced/FAQ.html#%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86%E4%BD%BF%E7%94%A8%E8%83%A1%E6%A1%83%E5%B7%A5%E5%85%B7%E7%AE%B1
https://github.com/HolographicHat/GetToken/releases/latest
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs
index 3cee1312..73a854c9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs
@@ -1,6 +1,8 @@
-// Copyright (c) DGP Studio. All rights reserved.
+// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Web;
using Snap.Hutao.Win32.Registry;
using System.Net;
using System.Reflection;
@@ -8,43 +10,38 @@ using System.Reflection;
namespace Snap.Hutao.Core.IO.Http.DynamicProxy;
[Injection(InjectAs.Singleton)]
-internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
+internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, IDisposable
{
private const string ProxySettingPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections";
- private static readonly MethodInfo ConstructSystemProxyMethod;
+ private static readonly Lazy LazyConstructSystemProxyMethod = new(GetConstructSystemProxyMethod);
+ private readonly IServiceProvider serviceProvider;
private readonly RegistryWatcher watcher;
private IWebProxy innerProxy = default!;
- [SuppressMessage("", "CA1810")]
- static DynamicHttpProxy()
+ public DynamicHttpProxy(IServiceProvider serviceProvider)
{
- Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo");
- ArgumentNullException.ThrowIfNull(systemProxyInfoType);
+ this.serviceProvider = serviceProvider;
+ UpdateInnerProxy();
- MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public);
- ArgumentNullException.ThrowIfNull(constructSystemProxyMethod);
- ConstructSystemProxyMethod = constructSystemProxyMethod;
- }
-
- public DynamicHttpProxy()
- {
- UpdateProxy();
-
- watcher = new(ProxySettingPath, UpdateProxy);
+ watcher = new(ProxySettingPath, OnSystemProxySettingsChanged);
watcher.Start();
}
- ///
- public ICredentials? Credentials
+ public string CurrentProxyUri
{
- get => InnerProxy.Credentials;
- set => InnerProxy.Credentials = value;
+ get
+ {
+ Uri? proxyUri = GetProxy("https://hut.ao".ToUri());
+ return proxyUri is null
+ ? SH.ViewPageFeedbackCurrentProxyNoProxyDescription
+ : proxyUri.AbsoluteUri;
+ }
}
- private IWebProxy InnerProxy
+ public IWebProxy InnerProxy
{
get => innerProxy;
@@ -61,13 +58,10 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
}
}
- [MemberNotNull(nameof(innerProxy))]
- public void UpdateProxy()
+ public ICredentials? Credentials
{
- IWebProxy? proxy = ConstructSystemProxyMethod.Invoke(default, default) as IWebProxy;
- ArgumentNullException.ThrowIfNull(proxy);
-
- InnerProxy = proxy;
+ get => InnerProxy.Credentials;
+ set => InnerProxy.Credentials = value;
}
public Uri? GetProxy(Uri destination)
@@ -85,4 +79,33 @@ internal sealed partial class DynamicHttpProxy : IWebProxy, IDisposable
(innerProxy as IDisposable)?.Dispose();
watcher.Dispose();
}
+
+ public void OnSystemProxySettingsChanged()
+ {
+ UpdateInnerProxy();
+
+ // TaskContext can't be injected directly since there are some recursive dependencies.
+ ITaskContext taskContext = serviceProvider.GetRequiredService();
+ taskContext.BeginInvokeOnMainThread(() => OnPropertyChanged(nameof(CurrentProxyUri)));
+ }
+
+ private static MethodInfo GetConstructSystemProxyMethod()
+ {
+ Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo");
+ ArgumentNullException.ThrowIfNull(systemProxyInfoType);
+
+ MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public);
+ ArgumentNullException.ThrowIfNull(constructSystemProxyMethod);
+
+ return constructSystemProxyMethod;
+ }
+
+ [MemberNotNull(nameof(innerProxy))]
+ private void UpdateInnerProxy()
+ {
+ IWebProxy? proxy = LazyConstructSystemProxyMethod.Value.Invoke(default, default) as IWebProxy;
+ ArgumentNullException.ThrowIfNull(proxy);
+
+ InnerProxy = proxy;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs
new file mode 100644
index 00000000..607349aa
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs
@@ -0,0 +1,85 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Win32.Foundation;
+using Snap.Hutao.Win32.NetworkManagement.WindowsFirewall;
+using Snap.Hutao.Win32.Security;
+using System.Runtime.InteropServices;
+using static Snap.Hutao.Win32.AdvApi32;
+using static Snap.Hutao.Win32.ApiMsWinNetIsolation;
+using static Snap.Hutao.Win32.Macros;
+
+namespace Snap.Hutao.Core.IO.Http.Loopback;
+
+[Injection(InjectAs.Singleton)]
+internal sealed unsafe class LoopbackManager : ObservableObject
+{
+ private readonly RuntimeOptions runtimeOptions;
+ private readonly ITaskContext taskContext;
+
+ private readonly SID hutaoContainerSID;
+ private bool isLoopbackEnabled;
+
+ public LoopbackManager(IServiceProvider serviceProvider)
+ {
+ runtimeOptions = serviceProvider.GetRequiredService();
+ taskContext = serviceProvider.GetRequiredService();
+
+ INET_FIREWALL_APP_CONTAINER* pContainers = default;
+ try
+ {
+ WIN32_ERROR error = NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers);
+ Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error));
+ for (uint i = 0; i < acCount; i++)
+ {
+ INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i;
+ ReadOnlySpan appContainerName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pContainer->appContainerName);
+ if (appContainerName.Equals(runtimeOptions.FamilyName))
+ {
+ hutaoContainerSID = *pContainer->appContainerSid;
+ break;
+ }
+ }
+ }
+ finally
+ {
+ uint retVal = NetworkIsolationFreeAppContainers(pContainers);
+ Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(*(WIN32_ERROR*)&retVal));
+ }
+
+ WIN32_ERROR error2 = NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
+ Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error2));
+ fixed (SID* phutaoContainerSID = &hutaoContainerSID)
+ {
+ for (uint i = 0; i < accCount; i++)
+ {
+ if (EqualSid(phutaoContainerSID, (pSids + i)->Sid))
+ {
+ IsLoopbackEnabled = true;
+ break;
+ }
+ }
+ }
+ }
+
+ public bool IsLoopbackEnabled { get => isLoopbackEnabled; private set => SetProperty(ref isLoopbackEnabled, value); }
+
+ public void EnableLoopback()
+ {
+ NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
+ List sids = new((int)(accCount + 1));
+ for (uint i = 0; i < accCount; i++)
+ {
+ sids.Add(*(pSids + i));
+ }
+
+ fixed (SID* phutaoContainerSID = &hutaoContainerSID)
+ {
+ SID_AND_ATTRIBUTES sidAndAttributes = default;
+ sidAndAttributes.Sid = phutaoContainerSID;
+ sids.Add(sidAndAttributes);
+ IsLoopbackEnabled = NetworkIsolationSetAppContainerConfig(CollectionsMarshal.AsSpan(sids)) is WIN32_ERROR.ERROR_SUCCESS;
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
index 321a9291..744206bf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.en.resx
@@ -1,17 +1,17 @@
-
@@ -1862,6 +1862,12 @@
Useful Links
+
+ Current Proxy
+
+
+ No Proxy
+
Keep in touch with us
@@ -3008,4 +3014,4 @@
Monitor ID
-
\ No newline at end of file
+
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index b801ff1a..c2094ee5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -1187,6 +1187,12 @@
实时便笺 Webhook Url
+
+ 解除限制后需要使用其他工具恢复限制
+
+
+ 是否解除 Loopback 限制
+
导入祈愿记录
@@ -1862,6 +1868,18 @@
常用链接
+
+ 当前代理
+
+
+ 无代理
+
+
+ 已解除
+
+
+ 解除 Loopback 限制
+
与我们密切联系
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml
index 05d6783a..943f8172 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml
@@ -3,7 +3,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:clw="using:CommunityToolkit.Labs.WinUI"
- xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
+ xmlns:cwcont="using:CommunityToolkit.WinUI.Controls"
+ xmlns:cwconv="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
@@ -28,6 +29,15 @@
+
+
+
+
+
+
+
+
+
@@ -41,152 +51,162 @@
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
-
-
+
+
-
-
-
+
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs
index aa5b5b65..e80b8edb 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs
@@ -1,8 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core;
using Snap.Hutao.Core.IO.DataTransfer;
+using Snap.Hutao.Core.IO.Http.DynamicProxy;
+using Snap.Hutao.Core.IO.Http.Loopback;
+using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Service;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Web.Hutao;
@@ -19,7 +23,10 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel
{
private readonly HutaoInfrastructureClient hutaoInfrastructureClient;
private readonly HutaoDocumentationClient hutaoDocumentationClient;
+ private readonly IContentDialogFactory contentDialogFactory;
private readonly IClipboardProvider clipboardProvider;
+ private readonly DynamicHttpProxy dynamicHttpProxy;
+ private readonly LoopbackManager loopbackManager;
private readonly IInfoBarService infoBarService;
private readonly CultureOptions cultureOptions;
private readonly RuntimeOptions runtimeOptions;
@@ -31,6 +38,10 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel
public RuntimeOptions RuntimeOptions { get => runtimeOptions; }
+ public DynamicHttpProxy DynamicHttpProxy { get => dynamicHttpProxy; }
+
+ public LoopbackManager LoopbackManager { get => loopbackManager; }
+
public string? SearchText { get => searchText; set => SetProperty(ref searchText, value); }
public List? SearchResults { get => searchResults; set => SetProperty(ref searchResults, value); }
@@ -109,4 +120,18 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel
infoBarService.Error(ex);
}
}
+
+ [Command("EnableLoopbackCommand")]
+ private async Task EnableLoopbackAsync()
+ {
+ ContentDialogResult result = await contentDialogFactory
+ .CreateForConfirmCancelAsync(SH.ViewDialogFeedbackEnableLoopbackTitle, SH.ViewDialogFeedbackEnableLoopbackContent)
+ .ConfigureAwait(false);
+
+ if (result is ContentDialogResult.Primary)
+ {
+ await taskContext.SwitchToMainThreadAsync();
+ LoopbackManager.EnableLoopback();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs b/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs
index de928361..d95aa0e4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs
@@ -13,6 +13,10 @@ namespace Snap.Hutao.Win32;
[SuppressMessage("", "SYSLIB1054")]
internal static class AdvApi32
{
+ [DllImport("ADVAPI32.dll", ExactSpelling = true, SetLastError = true)]
+ [SupportedOSPlatform("windows5.1.2600")]
+ public static extern BOOL EqualSid(PSID pSid1, PSID pSid2);
+
[DllImport("ADVAPI32.dll", ExactSpelling = true)]
[SupportedOSPlatform("windows5.0")]
public static extern WIN32_ERROR RegCloseKey(HKEY hKey);
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs b/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs
index a4259ab2..29fffef3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs
@@ -20,13 +20,14 @@ internal static class ApiMsWinNetIsolation
public static unsafe extern uint NetworkIsolationEnumAppContainers(uint Flags, uint* pdwNumPublicAppCs, INET_FIREWALL_APP_CONTAINER** ppPublicAppCs);
[DebuggerStepThrough]
- public static unsafe uint NetworkIsolationEnumAppContainers(NETISO_FLAG Flags, out uint dwNumPublicAppCs, out INET_FIREWALL_APP_CONTAINER* pPublicAppCs)
+ public static unsafe WIN32_ERROR NetworkIsolationEnumAppContainers(NETISO_FLAG Flags, out uint dwNumPublicAppCs, out INET_FIREWALL_APP_CONTAINER* pPublicAppCs)
{
fixed (uint* pdwNumPublicAppCs = &dwNumPublicAppCs)
{
fixed (INET_FIREWALL_APP_CONTAINER** ppPublicAppCs = &pPublicAppCs)
{
- return NetworkIsolationEnumAppContainers((uint)Flags, pdwNumPublicAppCs, ppPublicAppCs);
+ uint retVal = NetworkIsolationEnumAppContainers((uint)Flags, pdwNumPublicAppCs, ppPublicAppCs);
+ return *(WIN32_ERROR*)&retVal;
}
}
}
@@ -36,11 +37,12 @@ internal static class ApiMsWinNetIsolation
public static unsafe extern uint NetworkIsolationFreeAppContainers(INET_FIREWALL_APP_CONTAINER* pPublicAppCs);
[DebuggerStepThrough]
- public static unsafe uint NetworkIsolationFreeAppContainers(ref readonly INET_FIREWALL_APP_CONTAINER publicAppCs)
+ public static unsafe WIN32_ERROR NetworkIsolationFreeAppContainers(ref readonly INET_FIREWALL_APP_CONTAINER publicAppCs)
{
fixed (INET_FIREWALL_APP_CONTAINER* pPublicAppCs = &publicAppCs)
{
- return NetworkIsolationFreeAppContainers(pPublicAppCs);
+ uint retVal = NetworkIsolationFreeAppContainers(pPublicAppCs);
+ return *(WIN32_ERROR*)&retVal;
}
}
@@ -49,13 +51,14 @@ internal static class ApiMsWinNetIsolation
public static unsafe extern uint NetworkIsolationGetAppContainerConfig(uint* pdwNumPublicAppCs, SID_AND_ATTRIBUTES** appContainerSids);
[DebuggerStepThrough]
- public static unsafe uint NetworkIsolationGetAppContainerConfig(out uint dwNumPublicAppCs, out SID_AND_ATTRIBUTES* appContainerSids)
+ public static unsafe WIN32_ERROR NetworkIsolationGetAppContainerConfig(out uint dwNumPublicAppCs, out SID_AND_ATTRIBUTES* appContainerSids)
{
fixed (uint* pdwNumPublicAppCs = &dwNumPublicAppCs)
{
fixed (SID_AND_ATTRIBUTES** pAppContainerSids = &appContainerSids)
{
- return NetworkIsolationGetAppContainerConfig(pdwNumPublicAppCs, pAppContainerSids);
+ uint retVal = NetworkIsolationGetAppContainerConfig(pdwNumPublicAppCs, pAppContainerSids);
+ return *(WIN32_ERROR*)&retVal;
}
}
}
@@ -65,11 +68,12 @@ internal static class ApiMsWinNetIsolation
public static unsafe extern uint NetworkIsolationSetAppContainerConfig(uint dwNumPublicAppCs, SID_AND_ATTRIBUTES* appContainerSids);
[DebuggerStepThrough]
- public static unsafe uint NetworkIsolationSetAppContainerConfig(ReadOnlySpan appContainerSids)
+ public static unsafe WIN32_ERROR NetworkIsolationSetAppContainerConfig(ReadOnlySpan appContainerSids)
{
fixed (SID_AND_ATTRIBUTES* pAppContainerSids = appContainerSids)
{
- return NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids);
+ uint retVal = NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids);
+ return *(WIN32_ERROR*)&retVal;
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs
index e4a4ba5e..97e163ec 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs
@@ -3,8 +3,8 @@
namespace Snap.Hutao.Win32.Foundation;
-internal unsafe struct FlexibleArray
- where T : unmanaged
+internal unsafe struct FlexibleArray
+ where TElement : unmanaged
{
- public T* Reference;
+ public TElement* Reference;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs
index 8978cf35..f039093d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs
@@ -1,9 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Win32.Security;
+
namespace Snap.Hutao.Win32.Foundation;
internal struct PSID
{
public unsafe void* Value;
+
+ public static unsafe implicit operator PSID(SID* value) => *(PSID*)&value;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/Common/SHITEMID.cs b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/Common/SHITEMID.cs
index 9c13d012..0efd13a6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/Common/SHITEMID.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/Common/SHITEMID.cs
@@ -1,8 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using System.Runtime.InteropServices;
using Snap.Hutao.Win32.Foundation;
+using System.Runtime.InteropServices;
namespace Snap.Hutao.Win32.UI.Shell.Common;