mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Merge pull request #1286 from DGP-Studio/feat/feedback_network
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
|
||||
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_Loopback">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</x:String>
|
||||
|
||||
<!-- Other -->
|
||||
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
|
||||
|
||||
@@ -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<MethodInfo> 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();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<ITaskContext>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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<RuntimeOptions>();
|
||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
|
||||
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<char> 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<SID_AND_ATTRIBUTES> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -1862,6 +1862,12 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>Useful Links</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>Current Proxy</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>No Proxy</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Keep in touch with us</value>
|
||||
</data>
|
||||
@@ -3008,4 +3014,4 @@
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>Monitor ID</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@@ -1187,6 +1187,12 @@
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>导入祈愿记录</value>
|
||||
</data>
|
||||
@@ -1862,6 +1868,18 @@
|
||||
<data name="ViewPageFeedbackCommonLinksHeader" xml:space="preserve">
|
||||
<value>常用链接</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>与我们密切联系</value>
|
||||
</data>
|
||||
|
||||
@@ -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 @@
|
||||
<SolidColorBrush x:Key="SettingsCardBackgroundDisabled" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="SettingsCardBackgroundPointerOver" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="SettingsCardBackgroundPressed" Color="Transparent"/>
|
||||
|
||||
<cwconv:BoolToObjectConverter x:Key="BoolToLoopbackDescriptionControlConverter">
|
||||
<cwconv:BoolToObjectConverter.TrueValue>
|
||||
<TextBlock Text="{shcm:ResourceString Name=ViewPageFeedbackEnableLoopbackEnabledDescription}"/>
|
||||
</cwconv:BoolToObjectConverter.TrueValue>
|
||||
<cwconv:BoolToObjectConverter.FalseValue>
|
||||
<HyperlinkButton Content="{shcm:ResourceString Name=ViewPageAnnouncementViewDetails}" NavigateUri="{StaticResource DocumentLink_Loopback}"/>
|
||||
</cwconv:BoolToObjectConverter.FalseValue>
|
||||
</cwconv:BoolToObjectConverter>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
@@ -41,152 +51,162 @@
|
||||
<ScrollViewer>
|
||||
<StackPanel Margin="16" Spacing="3">
|
||||
<Border Style="{ThemeResource AcrylicBorderlessBorderCardStyle}">
|
||||
<cwc:SettingsExpander
|
||||
<cwcont:SettingsExpander
|
||||
Description="{Binding RuntimeOptions.Version}"
|
||||
Header="{shcm:ResourceString Name=AppName}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsExpander.Items>
|
||||
<cwcont:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingCopyDeviceIdAction}"
|
||||
Command="{Binding CopyDeviceIdCommand}"
|
||||
Description="{Binding RuntimeOptions.DeviceId}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingDeviceIdHeader}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard Description="{Binding IPInformation}" Header="{shcm:ResourceString Name=ViewPageSettingDeviceIpHeader}"/>
|
||||
<cwc:SettingsCard Description="{Binding RuntimeOptions.WebView2Version}" Header="{shcm:ResourceString Name=ViewPageSettingWebview2Header}"/>
|
||||
</cwc:SettingsExpander.Items>
|
||||
</cwc:SettingsExpander>
|
||||
<cwcont:SettingsCard Description="{Binding IPInformation}" Header="{shcm:ResourceString Name=ViewPageSettingDeviceIpHeader}"/>
|
||||
<cwcont:SettingsCard Description="{Binding DynamicHttpProxy.CurrentProxy}" Header="{shcm:ResourceString Name=ViewPageFeedbackCurrentProxyHeader}"/>
|
||||
<cwcont:SettingsCard
|
||||
Command="{Binding EnableLoopbackCommand}"
|
||||
Header="{shcm:ResourceString Name=ViewPageFeedbackEnableLoopbackHeader}"
|
||||
IsClickEnabled="True"
|
||||
IsEnabled="{Binding LoopbackManager.IsLoopbackEnabled, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<cwcont:SettingsCard.Description>
|
||||
<UserControl Content="{Binding LoopbackManager.IsLoopbackEnabled, Converter={StaticResource BoolToLoopbackDescriptionControlConverter}, Mode=OneWay}"/>
|
||||
</cwcont:SettingsCard.Description>
|
||||
</cwcont:SettingsCard>
|
||||
<cwcont:SettingsCard Description="{Binding RuntimeOptions.WebView2Version}" Header="{shcm:ResourceString Name=ViewPageSettingWebview2Header}"/>
|
||||
</cwcont:SettingsExpander.Items>
|
||||
</cwcont:SettingsExpander>
|
||||
</Border>
|
||||
<Border Style="{ThemeResource AcrylicBorderlessBorderCardStyle}">
|
||||
<cwc:SettingsExpander
|
||||
<cwcont:SettingsExpander
|
||||
Description="{shcm:ResourceString Name=ViewPageFeedbackEngageWithUsDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageFeedbackCommonLinksHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsExpander.Items>
|
||||
<cwcont:SettingsCard
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://github.com/DGP-Studio/Snap.Hutao/issues/new/choose"
|
||||
Description="{shcm:ResourceString Name=ViewPageFeedbackGithubIssuesDescription}"
|
||||
Header="GitHub Issues"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://github.com/orgs/DGP-Studio/projects/2"
|
||||
Description="{shcm:ResourceString Name=ViewPageFeedbackRoadmapDescription}"
|
||||
Header="GitHub Projects"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://status.hut.ao"
|
||||
Description="{shcm:ResourceString Name=ViewPageFeedbackServerStatusDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageFeedbackServerStatusHeader}"
|
||||
IsClickEnabled="True"/>
|
||||
</cwc:SettingsExpander.Items>
|
||||
</cwc:SettingsExpander>
|
||||
</cwcont:SettingsExpander.Items>
|
||||
</cwcont:SettingsExpander>
|
||||
</Border>
|
||||
<Border Style="{ThemeResource AcrylicBorderlessBorderCardStyle}">
|
||||
<cwc:SettingsExpander
|
||||
<cwcont:SettingsExpander
|
||||
Header="{shcm:ResourceString Name=ViewPageFeedbackFeatureGuideHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsExpander.Items>
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/dashboard.html"
|
||||
Header="{shcm:ResourceString Name=ViewAnnouncementHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/Announcement.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/game-launcher.html"
|
||||
Header="{shcm:ResourceString Name=ViewLaunchGameHeader}"
|
||||
IsClickEnabled="True">
|
||||
<cwc:SettingsCard.HeaderIcon>
|
||||
<cwcont:SettingsCard.HeaderIcon>
|
||||
<!-- This icon is not a square -->
|
||||
<BitmapIcon
|
||||
Width="24"
|
||||
Height="24"
|
||||
ShowAsMonochrome="False"
|
||||
UriSource="ms-appx:///Resource/Navigation/LaunchGame.png"/>
|
||||
</cwc:SettingsCard.HeaderIcon>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
</cwcont:SettingsCard.HeaderIcon>
|
||||
</cwcont:SettingsCard>
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/wish-export.html"
|
||||
Header="{shcm:ResourceString Name=ViewGachaLogHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/GachaLog.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/achievements.html"
|
||||
Header="{shcm:ResourceString Name=ViewAchievementHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/Achievement.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/real-time-notes.html"
|
||||
Header="{shcm:ResourceString Name=ViewDailyNoteHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/DailyNote.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/character-data.html"
|
||||
Header="{shcm:ResourceString Name=ViewAvatarPropertyHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/AvatarProperty.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/hutao-API.html"
|
||||
Header="{shcm:ResourceString Name=ViewSpiralAbyssHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/SpiralAbyss.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/develop-plan.html"
|
||||
Header="{shcm:ResourceString Name=ViewCultivationHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/Cultivation.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/character-wiki.html"
|
||||
Header="{shcm:ResourceString Name=ViewWikiAvatarHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiAvatar.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/weapon-wiki.html"
|
||||
Header="{shcm:ResourceString Name=ViewWikiWeaponHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiWeapon.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/monster-wiki.html"
|
||||
Header="{shcm:ResourceString Name=ViewWikiMonsterHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiMonster.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwc:SettingsCard
|
||||
<cwcont:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
Command="{Binding NavigateToUriCommand}"
|
||||
CommandParameter="https://hut.ao/features/hutao-settings.html"
|
||||
Header="{shcm:ResourceString Name=ViewSettingHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
</cwc:SettingsExpander.Items>
|
||||
</cwc:SettingsExpander>
|
||||
</cwcont:SettingsExpander.Items>
|
||||
</cwcont:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -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<AlgoliaHit>? 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<SID_AND_ATTRIBUTES> appContainerSids)
|
||||
public static unsafe WIN32_ERROR NetworkIsolationSetAppContainerConfig(ReadOnlySpan<SID_AND_ATTRIBUTES> appContainerSids)
|
||||
{
|
||||
fixed (SID_AND_ATTRIBUTES* pAppContainerSids = appContainerSids)
|
||||
{
|
||||
return NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids);
|
||||
uint retVal = NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids);
|
||||
return *(WIN32_ERROR*)&retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
namespace Snap.Hutao.Win32.Foundation;
|
||||
|
||||
internal unsafe struct FlexibleArray<T>
|
||||
where T : unmanaged
|
||||
internal unsafe struct FlexibleArray<TElement>
|
||||
where TElement : unmanaged
|
||||
{
|
||||
public T* Reference;
|
||||
public TElement* Reference;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user