diff --git a/BetterGenshinImpact/Hutao/BGINamedPipe.cs b/BetterGenshinImpact/Hutao/BGINamedPipe.cs new file mode 100644 index 00000000..caf6454a --- /dev/null +++ b/BetterGenshinImpact/Hutao/BGINamedPipe.cs @@ -0,0 +1,109 @@ +using BetterGenshinImpact.Helpers; +using BetterGenshinImpact.View.Pages; +using BetterGenshinImpact.ViewModel.Pages; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.IO.Pipes; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using Wpf.Ui; + +namespace BetterGenshinImpact.Hutao; + +internal sealed partial class BGINamedPipe : IDisposable +{ + private const int Version = 1; + + private readonly IServiceProvider serviceProvider; + private readonly ILogger logger; + + private readonly CancellationTokenSource serverTokenSource = new(); + private readonly TaskCompletionSource serverRunTaskCompletionSource = new(); + + private readonly NamedPipeServerStream serverStream; + + public BGINamedPipe(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + logger = serviceProvider.GetRequiredService>(); + + PipeSecurity? pipeSecurity = default; + + if (RuntimeHelper.IsElevated) + { + SecurityIdentifier everyOne = new(WellKnownSidType.WorldSid, null); + + pipeSecurity = new(); + pipeSecurity.AddAccessRule(new PipeAccessRule(everyOne, PipeAccessRights.FullControl, AccessControlType.Allow)); + } + + serverStream = NamedPipeServerStreamAcl.Create( + "BetterGenshinImpact.NamedPipe", + PipeDirection.InOut, + NamedPipeServerStream.MaxAllowedServerInstances, + PipeTransmissionMode.Byte, + PipeOptions.Asynchronous | PipeOptions.WriteThrough, + 0, + 0, + pipeSecurity); + } + + public void Dispose() + { + serverTokenSource.Cancel(); + serverRunTaskCompletionSource.Task.GetAwaiter().GetResult(); + serverTokenSource.Dispose(); + serverStream.Dispose(); + } + + public async ValueTask RunAsync() + { + while (!serverTokenSource.IsCancellationRequested) + { + try + { + await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false); + logger.LogInformation("Pipe session created"); + RunPacketSession(serverStream, serverTokenSource.Token); + } + catch (OperationCanceledException) + { + } + } + + serverRunTaskCompletionSource.TrySetResult(); + } + + private void RunPacketSession(NamedPipeServerStream serverStream, CancellationToken token) + { + while (serverStream.IsConnected && !token.IsCancellationRequested) + { + serverStream.ReadPacket(out PipePacketHeader header); + logger.LogInformation("Pipe packet: [Type:{Type}] [Command:{Command}]", header.Type, header.Command); + switch ((header.Type, header.Command)) + { + case (PipePacketType.Request, PipePacketCommand.SnapHutaoToBetterGenshinImpactRequest): + if (serverStream.ReadJsonContent(in header) is { } request) + { + DispatchHutaoRequest(request); + } + + break; + } + } + } + + private void DispatchHutaoRequest(HutaoRequest request) + { + switch (request.Kind) + { + case HutaoRequestKind.StartCapture: + HomePageViewModel home = serviceProvider.GetRequiredService(); + home.Start((nint)request.Data.GetInt64()); + break; + } + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Hutao/HutaoNamedPipe.cs b/BetterGenshinImpact/Hutao/HutaoNamedPipe.cs index 87929f13..d8fa570b 100644 --- a/BetterGenshinImpact/Hutao/HutaoNamedPipe.cs +++ b/BetterGenshinImpact/Hutao/HutaoNamedPipe.cs @@ -1,17 +1,21 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.IO.Pipes; using System.Text; +using System.Text.Json.Serialization; namespace BetterGenshinImpact.Hutao; -internal sealed class HutaoNamedPipe : IDisposable +internal sealed partial class HutaoNamedPipe : IDisposable { + private const int Version = 1; + private readonly NamedPipeClientStream clientStream = new(".", "Snap.Hutao.PrivateNamedPipe", PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough); private readonly IServiceProvider serviceProvider; - private Lazy isSupported; + private readonly Lazy isSupported; public HutaoNamedPipe(IServiceProvider serviceProvider) { diff --git a/BetterGenshinImpact/Hutao/HutaoRequest.cs b/BetterGenshinImpact/Hutao/HutaoRequest.cs new file mode 100644 index 00000000..e7abfa11 --- /dev/null +++ b/BetterGenshinImpact/Hutao/HutaoRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json; + +namespace BetterGenshinImpact.Hutao; + +internal sealed class HutaoRequest +{ + // DO NOT RENAME: Json convert compatibility + public HutaoRequestKind Kind { get; set; } + + // DO NOT RENAME: Json convert compatibility + public JsonElement Data { get; set; } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Hutao/HutaoRequestKind.cs b/BetterGenshinImpact/Hutao/HutaoRequestKind.cs new file mode 100644 index 00000000..97a6febd --- /dev/null +++ b/BetterGenshinImpact/Hutao/HutaoRequestKind.cs @@ -0,0 +1,7 @@ +namespace BetterGenshinImpact.Hutao; + +internal enum HutaoRequestKind +{ + None = 0, + StartCapture = 1, +} \ No newline at end of file diff --git a/BetterGenshinImpact/Hutao/PipePacketCommand.cs b/BetterGenshinImpact/Hutao/PipePacketCommand.cs index 2b1c5c44..5c45a919 100644 --- a/BetterGenshinImpact/Hutao/PipePacketCommand.cs +++ b/BetterGenshinImpact/Hutao/PipePacketCommand.cs @@ -1,6 +1,12 @@ namespace BetterGenshinImpact.Hutao; +// Snap Hutao reserves all variables that don't appear below +// Any command may add in furure should not send to Snap Hutao Pipe internal enum PipePacketCommand : byte { None = 0, + BetterGenshinImpactToSnapHutaoRequest = 20, + BetterGenshinImpactToSnapHutaoResponse = 21, + SnapHutaoToBetterGenshinImpactRequest = 22, + SnapHutaoToBetterGenshinImpactResponse = 23, } \ No newline at end of file diff --git a/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs index f27dd987..3e707f8a 100644 --- a/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs @@ -223,7 +223,7 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi Start(hWnd); } - private void Start(IntPtr hWnd) + internal void Start(IntPtr hWnd) { Debug.WriteLine($"原神启动句柄{hWnd}"); lock (this)