mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
switch to main thread async
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.WinUI;
|
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
@@ -14,11 +13,12 @@ namespace Snap.Hutao.Core.LifeCycle;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class Activation
|
internal static class Activation
|
||||||
{
|
{
|
||||||
public static volatile bool isActivating = false;
|
private static volatile bool isActivating = false;
|
||||||
public static object activationLock = new object();
|
private static object activationLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 响应激活事件
|
/// 响应激活事件
|
||||||
|
/// 激活事件一般不会在UI线程上触发
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">发送方</param>
|
/// <param name="sender">发送方</param>
|
||||||
/// <param name="args">激活参数</param>
|
/// <param name="args">激活参数</param>
|
||||||
@@ -49,19 +49,7 @@ internal static class Activation
|
|||||||
isActivating = true;
|
isActivating = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.Window = Ioc.Default.GetRequiredService<MainWindow>();
|
await HandleActivationCoreAsync(args).ConfigureAwait(false);
|
||||||
|
|
||||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
|
||||||
await infoBarService.WaitInitializationAsync();
|
|
||||||
|
|
||||||
if (args.Kind == ExtendedActivationKind.Protocol)
|
|
||||||
{
|
|
||||||
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
|
||||||
{
|
|
||||||
infoBarService.Information(uri.ToString());
|
|
||||||
await HandleUrlActivationAsync(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (activationLock)
|
lock (activationLock)
|
||||||
{
|
{
|
||||||
@@ -69,20 +57,37 @@ internal static class Activation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task HandleActivationCoreAsync(AppActivationArguments args)
|
||||||
|
{
|
||||||
|
App.Window = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
|
|
||||||
|
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||||
|
await infoBarService.WaitInitializationAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (args.Kind == ExtendedActivationKind.Protocol)
|
||||||
|
{
|
||||||
|
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
||||||
|
{
|
||||||
|
infoBarService.Information(uri.ToString());
|
||||||
|
await HandleUrlActivationAsync(uri).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task HandleUrlActivationAsync(Uri uri)
|
private static async Task HandleUrlActivationAsync(Uri uri)
|
||||||
{
|
{
|
||||||
UriBuilder builder = new(uri);
|
UriBuilder builder = new(uri);
|
||||||
Requires.Argument(builder.Scheme == "hutao", nameof(uri), "uri 的协议不正确");
|
Must.Argument(builder.Scheme == "hutao", "uri 的协议不正确");
|
||||||
|
|
||||||
string category = builder.Host.ToLowerInvariant();
|
string category = builder.Host.ToLowerInvariant();
|
||||||
string action = builder.Path.ToLowerInvariant();
|
string action = builder.Path.ToLowerInvariant();
|
||||||
string rawParameter = builder.Query;
|
string rawParameter = builder.Query.ToLowerInvariant();
|
||||||
|
|
||||||
switch (category)
|
switch (category)
|
||||||
{
|
{
|
||||||
case "achievement":
|
case "achievement":
|
||||||
{
|
{
|
||||||
await HandleAchievementActionAsync(action, rawParameter);
|
await HandleAchievementActionAsync(action, rawParameter).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,13 +99,13 @@ internal static class Activation
|
|||||||
{
|
{
|
||||||
case "/import":
|
case "/import":
|
||||||
{
|
{
|
||||||
await Program.UIDispatcherQueue.EnqueueAsync(async () =>
|
await Program.SwitchToMainThreadAsync();
|
||||||
{
|
|
||||||
INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
|
INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
|
||||||
await Ioc.Default
|
await Ioc.Default
|
||||||
.GetRequiredService<INavigationService>()
|
.GetRequiredService<INavigationService>()
|
||||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true);
|
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||||
});
|
.ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Threading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步上下文等待体
|
||||||
|
/// </summary>
|
||||||
|
public struct SynchronizationContextAwaitable
|
||||||
|
{
|
||||||
|
private readonly SynchronizationContext context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的同步上下文等待体
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">同步上下文</param>
|
||||||
|
public SynchronizationContextAwaitable(SynchronizationContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取等待器
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>等待器</returns>
|
||||||
|
public SynchronizationContextAwaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return new SynchronizationContextAwaiter(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Threading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步上下文等待器
|
||||||
|
/// </summary>
|
||||||
|
public struct SynchronizationContextAwaiter : INotifyCompletion
|
||||||
|
{
|
||||||
|
private static readonly SendOrPostCallback PostCallback = state => ((Action)state!)();
|
||||||
|
private readonly SynchronizationContext context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的同步上下文等待器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">同步上下文</param>
|
||||||
|
public SynchronizationContextAwaiter(SynchronizationContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted => context == SynchronizationContext.Current;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 完成操作
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="continuation">后续操作</param>
|
||||||
|
[SuppressMessage("", "VSTHRD001")]
|
||||||
|
public void OnCompleted(Action continuation) => context.Post(PostCallback, continuation);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取执行结果
|
||||||
|
/// </summary>
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,4 +77,4 @@ public class Watcher : Observable
|
|||||||
watcher.IsCompleted = true;
|
watcher.IsCompleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,20 @@ namespace Snap.Hutao.Core.Validation;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Must
|
public static class Must
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Throws an <see cref="ArgumentException"/> if a condition does not evaluate to true.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="condition">The condition to check.</param>
|
||||||
|
/// <param name="message">message</param>
|
||||||
|
/// <param name="parameterName">The name of the parameter to blame in the exception, if thrown.</param>
|
||||||
|
public static void Argument([DoesNotReturnIf(false)] bool condition, string? message, [CallerArgumentExpression("condition")] string? parameterName = null)
|
||||||
|
{
|
||||||
|
if (!condition)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(message, parameterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unconditionally throws an <see cref="NotSupportedException"/>.
|
/// Unconditionally throws an <see cref="NotSupportedException"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ namespace Snap.Hutao.Core;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检测 WebView2运行时 是否存在
|
/// 检测 WebView2运行时 是否存在
|
||||||
/// 不再使用注册表检查方式
|
/// 不再使用注册表检查方式
|
||||||
|
/// 必须为抽象类才能使用泛型日志器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class WebView2Helper
|
internal abstract class WebView2Helper
|
||||||
{
|
{
|
||||||
private static bool hasEverDetected = false;
|
private static bool hasEverDetected = false;
|
||||||
private static bool isSupported = false;
|
private static bool isSupported = false;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Snap.Hutao.Core.Threading;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using WinRT;
|
using WinRT;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ namespace Snap.Hutao;
|
|||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private static volatile DispatcherQueue? dispatcherQueue;
|
private static volatile DispatcherQueue? dispatcherQueue;
|
||||||
|
private static volatile SynchronizationContext? context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主线程调度器队列
|
/// 主线程调度器队列
|
||||||
@@ -23,6 +25,15 @@ public static class Program
|
|||||||
get => Must.NotNull(dispatcherQueue!);
|
get => Must.NotNull(dispatcherQueue!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步切换到主线程
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>等待体</returns>
|
||||||
|
public static SynchronizationContextAwaitable SwitchToMainThreadAsync()
|
||||||
|
{
|
||||||
|
return new SynchronizationContextAwaitable(context!);
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport("Microsoft.ui.xaml.dll")]
|
[DllImport("Microsoft.ui.xaml.dll")]
|
||||||
private static extern void XamlCheckProcessRequirements();
|
private static extern void XamlCheckProcessRequirements();
|
||||||
|
|
||||||
@@ -36,7 +47,7 @@ public static class Program
|
|||||||
Application.Start(p =>
|
Application.Start(p =>
|
||||||
{
|
{
|
||||||
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
SynchronizationContext context = new DispatcherQueueSynchronizationContext(dispatcherQueue);
|
context = new DispatcherQueueSynchronizationContext(dispatcherQueue);
|
||||||
SynchronizationContext.SetSynchronizationContext(context);
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
_ = new App();
|
_ = new App();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -90,14 +90,15 @@ internal class InfoBarService : IInfoBarService
|
|||||||
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
|
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
|
[SuppressMessage("", "VSTHRD100")]
|
||||||
|
private async void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
|
||||||
{
|
{
|
||||||
if (infoBarStack is null)
|
if (infoBarStack is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoBarStack.DispatcherQueue.TryEnqueue(() => PrepareInfoBarAndShowInternal(severity, title, message, delay));
|
await PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -107,9 +108,10 @@ internal class InfoBarService : IInfoBarService
|
|||||||
/// <param name="title">标题</param>
|
/// <param name="title">标题</param>
|
||||||
/// <param name="message">消息</param>
|
/// <param name="message">消息</param>
|
||||||
/// <param name="delay">关闭延迟</param>
|
/// <param name="delay">关闭延迟</param>
|
||||||
[SuppressMessage("", "VSTHRD100", Justification = "只能通过 async void 方法使控件在主线程创建")]
|
private async Task PrepareInfoBarAndShowInternalAsync(InfoBarSeverity severity, string? title, string? message, int delay)
|
||||||
private async void PrepareInfoBarAndShowInternal(InfoBarSeverity severity, string? title, string? message, int delay)
|
|
||||||
{
|
{
|
||||||
|
await Program.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
InfoBar infoBar = new()
|
InfoBar infoBar = new()
|
||||||
{
|
{
|
||||||
Severity = severity,
|
Severity = severity,
|
||||||
@@ -128,12 +130,12 @@ internal class InfoBarService : IInfoBarService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
[SuppressMessage("", "VSTHRD100")]
|
||||||
|
private async void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
||||||
{
|
{
|
||||||
Must.NotNull(infoBarStack!).DispatcherQueue.TryEnqueue(() =>
|
await Program.SwitchToMainThreadAsync();
|
||||||
{
|
|
||||||
infoBarStack.Children.Remove(sender);
|
Must.NotNull(infoBarStack!).Children.Remove(sender);
|
||||||
sender.Closed -= OnInfoBarClosed;
|
sender.Closed -= OnInfoBarClosed;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user