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/Loopback/LoopbackManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs
new file mode 100644
index 00000000..6a109dc2
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs
@@ -0,0 +1,79 @@
+// 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 static Snap.Hutao.Win32.AdvApi32;
+using static Snap.Hutao.Win32.ApiMsWinNetIsolation;
+
+namespace Snap.Hutao.Core.IO.Http.Loopback;
+
+[Injection(InjectAs.Singleton)]
+internal sealed unsafe class LoopbackManager : ObservableObject, IDisposable
+{
+ private readonly INET_FIREWALL_APP_CONTAINER* pContainers;
+ private readonly INET_FIREWALL_APP_CONTAINER* pHutaoContainer;
+
+ private readonly RuntimeOptions runtimeOptions;
+ private readonly ITaskContext taskContext;
+
+
+ public LoopbackManager(IServiceProvider serviceProvider)
+ {
+ runtimeOptions = serviceProvider.GetRequiredService();
+ taskContext = serviceProvider.GetRequiredService();
+
+ NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers);
+ for (uint i = 0; i < acCount; i++)
+ {
+ INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i;
+ if (new string(pContainer->appContainerName) == runtimeOptions.FamilyName)
+ {
+ pHutaoContainer = pContainer;
+ break;
+ }
+ }
+
+ NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
+ for (uint i = 0; i < accCount; i++)
+ {
+ if (EqualSid(new PSID { Value = pHutaoContainer->appContainerSid }, (pSids + i)->Sid))
+ {
+ IsLoopbackEnabled = true;
+ break;
+ }
+ }
+ }
+
+ public bool IsLoopbackEnabled { get; private set; }
+
+ public void EnableLoopback()
+ {
+ List sids = [];
+
+ NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
+ for (uint i = 0; i < accCount; i++)
+ {
+ sids.Add(*(pSids + i));
+ }
+
+ sids.Add(new SID_AND_ATTRIBUTES
+ {
+ Sid = new PSID { Value = pHutaoContainer->appContainerSid },
+ Attributes = 0,
+ });
+
+ NetworkIsolationSetAppContainerConfig(sids.ToArray());
+
+ IsLoopbackEnabled = true;
+
+ taskContext.InvokeOnMainThread(() => OnPropertyChanged(nameof(IsLoopbackEnabled)));
+ }
+
+ public void Dispose()
+ {
+ _ = NetworkIsolationFreeAppContainers(pContainers);
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index 7ad45dfa..10abdd1b 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 限制
+
导入祈愿记录
@@ -1868,6 +1874,12 @@
无代理
+
+ 已启用
+
+
+ 解除 Loopback 限制
+
与我们密切联系
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/FeedbackPage.xaml
index 2a499ddb..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,153 +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 38df7dd0..7a7bd53a 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs
@@ -1,9 +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;
@@ -20,8 +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;
@@ -35,6 +40,8 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel
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); }
@@ -113,4 +120,17 @@ 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)
+ {
+ 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..5958a9dc 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)]
+ [SupportedOSPlatform("windows5.0")]
+ 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);