support Send on sync context

This commit is contained in:
Lightczx
2023-10-13 15:27:46 +08:00
parent 4e57520115
commit c0165c57fd
11 changed files with 123 additions and 103 deletions

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Windows.ApplicationModel.Resources;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Service;
using System.Globalization;

View File

@@ -29,11 +29,14 @@ internal sealed partial class ExceptionRecorder
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
serviceProvider
ValueTask<string?> task = serviceProvider
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
.UploadLogAsync(e.Exception)
.GetAwaiter()
.GetResult();
.UploadLogAsync(e.Exception);
if (!task.IsCompleted)
{
task.GetAwaiter().GetResult();
}
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
}

View File

@@ -15,7 +15,7 @@ internal sealed class RuntimeEnvironmentException : Exception
/// </summary>
/// <param name="message">消息</param>
/// <param name="innerException">内部错误</param>
public RuntimeEnvironmentException(string message, Exception innerException)
public RuntimeEnvironmentException(string message, Exception? innerException)
: base($"{message}\n{innerException.Message}", innerException)
{
}

View File

@@ -14,27 +14,27 @@ namespace Snap.Hutao.Core.ExceptionService;
[System.Diagnostics.StackTraceHidden]
internal static class ThrowHelper
{
/// <summary>
/// 操作取消
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="OperationCanceledException">操作取消异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
public static ArgumentException Argument(string message, string? paramName)
{
throw new OperationCanceledException(message, inner);
throw new ArgumentException(message, paramName);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception? inner)
{
throw new DatabaseCorruptedException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static GameFileOperationException GameFileOperation(string message, Exception? inner)
{
throw new GameFileOperationException(message, inner);
}
/// <summary>
/// 无效操作
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="InvalidOperationException">无效操作异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
@@ -42,71 +42,38 @@ internal static class ThrowHelper
throw new InvalidOperationException(message, inner);
}
/// <summary>
/// 游戏文件操作失败
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="GameFileOperationException">文件操作失败</exception>
public static GameFileOperationException GameFileOperation(string message, Exception inner)
{
throw new GameFileOperationException(message, inner);
}
/// <summary>
/// 包转换错误
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="PackageConvertException">包转换错误异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static PackageConvertException PackageConvert(string message, Exception inner)
{
throw new PackageConvertException(message, inner);
}
/// <summary>
/// 用户数据损坏
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
{
throw new UserdataCorruptedException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception inner)
{
throw new DatabaseCorruptedException(message, inner);
}
/// <summary>
/// 运行环境异常
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="RuntimeEnvironmentException">环境异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception inner)
{
throw new RuntimeEnvironmentException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static NotSupportedException NotSupported()
{
throw new NotSupportedException();
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
{
throw new OperationCanceledException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static PackageConvertException PackageConvert(string message, Exception? inner)
{
throw new PackageConvertException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception? inner)
{
throw new RuntimeEnvironmentException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static UserdataCorruptedException UserdataCorrupted(string message, Exception? inner)
{
throw new UserdataCorruptedException(message, inner);
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Dispatching;
using System.Runtime.ExceptionServices;
namespace Snap.Hutao.Core.Threading;
@@ -18,15 +19,35 @@ internal static class DispatcherQueueExtension
/// <param name="action">执行的回调</param>
public static void Invoke(this DispatcherQueue dispatcherQueue, Action action)
{
using (ManualResetEventSlim blockEvent = new())
if (dispatcherQueue.HasThreadAccess)
{
action();
return;
}
ExceptionDispatchInfo? exceptionDispatchInfo = null;
using (ManualResetEventSlim blockEvent = new(false))
{
dispatcherQueue.TryEnqueue(() =>
{
action();
blockEvent.Set();
try
{
action();
}
catch (Exception ex)
{
ExceptionDispatchInfo.Capture(ex);
}
finally
{
blockEvent.Set();
}
});
blockEvent.Wait();
#pragma warning disable CA1508
exceptionDispatchInfo?.Throw();
#pragma warning restore CA1508
}
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Dispatching;
namespace Snap.Hutao.Core.Threading;
internal sealed class DispatcherQueueSynchronizationContextSendSupport : SynchronizationContext
{
private readonly DispatcherQueue dispatcherQueue;
public DispatcherQueueSynchronizationContextSendSupport(DispatcherQueue dispatcherQueue)
{
this.dispatcherQueue = dispatcherQueue;
}
public override void Post(SendOrPostCallback d, object? state)
{
ArgumentNullException.ThrowIfNull(d);
dispatcherQueue.TryEnqueue(() => d(state));
}
public override void Send(SendOrPostCallback d, object? state)
{
ArgumentNullException.ThrowIfNull(d);
dispatcherQueue.Invoke(() => d(state));
}
public override SynchronizationContext CreateCopy()
{
return new DispatcherQueueSynchronizationContextSendSupport(dispatcherQueue);
}
}

View File

@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
internal sealed class TaskContext : ITaskContext
{
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
private readonly DispatcherQueueSynchronizationContextSendSupport synchronizationContext;
private readonly DispatcherQueue dispatcherQueue;
/// <summary>
@@ -20,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
public TaskContext()
{
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
synchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
}
/// <inheritdoc/>
@@ -39,18 +39,11 @@ internal sealed class TaskContext : ITaskContext
/// <inheritdoc/>
public void InvokeOnMainThread(Action action)
{
if (dispatcherQueue.HasThreadAccess)
{
action();
}
else
{
dispatcherQueue.Invoke(action);
}
dispatcherQueue.Invoke(action);
}
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
{
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
return new DispatcherQueueProgress<T>(handler, synchronizationContext);
}
}

View File

@@ -14,7 +14,7 @@ internal sealed class GameFileOperationException : Exception
/// </summary>
/// <param name="message">消息</param>
/// <param name="innerException">内部错误</param>
public GameFileOperationException(string message, Exception innerException)
public GameFileOperationException(string message, Exception? innerException)
: base(SH.ServiceGameFileOperationExceptionMessage.Format(message), innerException)
{
}

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Game.Package;
internal sealed class PackageConvertException : Exception
{
/// <inheritdoc cref="Exception(string?, Exception?)"/>
public PackageConvertException(string message, Exception innerException)
public PackageConvertException(string message, Exception? innerException)
: base(message, innerException)
{
}

View File

@@ -39,19 +39,20 @@ internal class MiHoYoJSInterface
document.querySelector('body').appendChild(st);
""";
private readonly SemaphoreSlim webMessageSemaphore = new(1);
private readonly Guid interfaceId = Guid.NewGuid();
private readonly IServiceProvider serviceProvider;
private readonly UserAndUid userAndUid;
private CoreWebView2 webView;
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
private readonly ILogger<MiHoYoJSInterface> logger;
private readonly SemaphoreSlim webMessageSemaphore = new(1);
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
private readonly TypedEventHandler<CoreWebView2, CoreWebView2NavigationStartingEventArgs> navigationStartingEventHandler;
private CoreWebView2 webView;
public MiHoYoJSInterface(CoreWebView2 webView, UserAndUid userAndUid)
{
// 由于Webview2 的作用域特殊性,我们在此处直接使用根服务

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Web.Request.Builder.Abstraction;
using System.Net.Http;
using System.Text;
@@ -66,7 +67,7 @@ internal abstract class HttpContentSerializer : IHttpContentSerializer, IHttpCon
The content to be serialized does not match the specified type.
Expected an instance of the class "{contentType.FullName}", but got "{actualContentType.FullName}".
""";
throw new ArgumentException(message, nameof(contentType));
ThrowHelper.Argument(message, nameof(contentType));
}
// The contentType is optional. In that case, try to get the type on our own.