mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support Send on sync context
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.Windows.ApplicationModel.Resources;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ internal sealed partial class ExceptionRecorder
|
|||||||
|
|
||||||
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
serviceProvider
|
ValueTask<string?> task = serviceProvider
|
||||||
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
|
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
|
||||||
.UploadLogAsync(e.Exception)
|
.UploadLogAsync(e.Exception);
|
||||||
.GetAwaiter()
|
|
||||||
.GetResult();
|
if (!task.IsCompleted)
|
||||||
|
{
|
||||||
|
task.GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
|
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ internal sealed class RuntimeEnvironmentException : Exception
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">消息</param>
|
/// <param name="message">消息</param>
|
||||||
/// <param name="innerException">内部错误</param>
|
/// <param name="innerException">内部错误</param>
|
||||||
public RuntimeEnvironmentException(string message, Exception innerException)
|
public RuntimeEnvironmentException(string message, Exception? innerException)
|
||||||
: base($"{message}\n{innerException.Message}", innerException)
|
: base($"{message}\n{innerException.Message}", innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,27 +14,27 @@ namespace Snap.Hutao.Core.ExceptionService;
|
|||||||
[System.Diagnostics.StackTraceHidden]
|
[System.Diagnostics.StackTraceHidden]
|
||||||
internal static class ThrowHelper
|
internal static class ThrowHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 操作取消
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">消息</param>
|
|
||||||
/// <param name="inner">内部错误</param>
|
|
||||||
/// <returns>nothing</returns>
|
|
||||||
/// <exception cref="OperationCanceledException">操作取消异常</exception>
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[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]
|
[DoesNotReturn]
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
|
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
|
||||||
@@ -42,71 +42,38 @@ internal static class ThrowHelper
|
|||||||
throw new InvalidOperationException(message, inner);
|
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]
|
[DoesNotReturn]
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static NotSupportedException NotSupported()
|
public static NotSupportedException NotSupported()
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Threading;
|
namespace Snap.Hutao.Core.Threading;
|
||||||
|
|
||||||
@@ -18,15 +19,35 @@ internal static class DispatcherQueueExtension
|
|||||||
/// <param name="action">执行的回调</param>
|
/// <param name="action">执行的回调</param>
|
||||||
public static void Invoke(this DispatcherQueue dispatcherQueue, Action action)
|
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(() =>
|
dispatcherQueue.TryEnqueue(() =>
|
||||||
{
|
{
|
||||||
action();
|
try
|
||||||
blockEvent.Set();
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionDispatchInfo.Capture(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
blockEvent.Set();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
blockEvent.Wait();
|
blockEvent.Wait();
|
||||||
|
#pragma warning disable CA1508
|
||||||
|
exceptionDispatchInfo?.Throw();
|
||||||
|
#pragma warning restore CA1508
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
|
|||||||
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
|
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
|
||||||
internal sealed class TaskContext : ITaskContext
|
internal sealed class TaskContext : ITaskContext
|
||||||
{
|
{
|
||||||
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
|
private readonly DispatcherQueueSynchronizationContextSendSupport synchronizationContext;
|
||||||
private readonly DispatcherQueue dispatcherQueue;
|
private readonly DispatcherQueue dispatcherQueue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
|
|||||||
public TaskContext()
|
public TaskContext()
|
||||||
{
|
{
|
||||||
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
|
synchronizationContext = new(dispatcherQueue);
|
||||||
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
|
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -39,18 +39,11 @@ internal sealed class TaskContext : ITaskContext
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void InvokeOnMainThread(Action action)
|
public void InvokeOnMainThread(Action action)
|
||||||
{
|
{
|
||||||
if (dispatcherQueue.HasThreadAccess)
|
dispatcherQueue.Invoke(action);
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dispatcherQueue.Invoke(action);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
|
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
|
||||||
{
|
{
|
||||||
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
|
return new DispatcherQueueProgress<T>(handler, synchronizationContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ internal sealed class GameFileOperationException : Exception
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">消息</param>
|
/// <param name="message">消息</param>
|
||||||
/// <param name="innerException">内部错误</param>
|
/// <param name="innerException">内部错误</param>
|
||||||
public GameFileOperationException(string message, Exception innerException)
|
public GameFileOperationException(string message, Exception? innerException)
|
||||||
: base(SH.ServiceGameFileOperationExceptionMessage.Format(message), innerException)
|
: base(SH.ServiceGameFileOperationExceptionMessage.Format(message), innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Game.Package;
|
|||||||
internal sealed class PackageConvertException : Exception
|
internal sealed class PackageConvertException : Exception
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="Exception(string?, Exception?)"/>
|
/// <inheritdoc cref="Exception(string?, Exception?)"/>
|
||||||
public PackageConvertException(string message, Exception innerException)
|
public PackageConvertException(string message, Exception? innerException)
|
||||||
: base(message, innerException)
|
: base(message, innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,19 +39,20 @@ internal class MiHoYoJSInterface
|
|||||||
document.querySelector('body').appendChild(st);
|
document.querySelector('body').appendChild(st);
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
private readonly SemaphoreSlim webMessageSemaphore = new(1);
|
||||||
private readonly Guid interfaceId = Guid.NewGuid();
|
private readonly Guid interfaceId = Guid.NewGuid();
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
private readonly UserAndUid userAndUid;
|
private readonly UserAndUid userAndUid;
|
||||||
private CoreWebView2 webView;
|
|
||||||
|
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
private readonly ILogger<MiHoYoJSInterface> logger;
|
private readonly ILogger<MiHoYoJSInterface> logger;
|
||||||
private readonly SemaphoreSlim webMessageSemaphore = new(1);
|
|
||||||
|
|
||||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
|
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
|
||||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
|
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
|
||||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2NavigationStartingEventArgs> navigationStartingEventHandler;
|
private readonly TypedEventHandler<CoreWebView2, CoreWebView2NavigationStartingEventArgs> navigationStartingEventHandler;
|
||||||
|
|
||||||
|
private CoreWebView2 webView;
|
||||||
|
|
||||||
public MiHoYoJSInterface(CoreWebView2 webView, UserAndUid userAndUid)
|
public MiHoYoJSInterface(CoreWebView2 webView, UserAndUid userAndUid)
|
||||||
{
|
{
|
||||||
// 由于Webview2 的作用域特殊性,我们在此处直接使用根服务
|
// 由于Webview2 的作用域特殊性,我们在此处直接使用根服务
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// 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 Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -66,7 +67,7 @@ internal abstract class HttpContentSerializer : IHttpContentSerializer, IHttpCon
|
|||||||
The content to be serialized does not match the specified type.
|
The content to be serialized does not match the specified type.
|
||||||
Expected an instance of the class "{contentType.FullName}", but got "{actualContentType.FullName}".
|
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.
|
// The contentType is optional. In that case, try to get the type on our own.
|
||||||
|
|||||||
Reference in New Issue
Block a user