code style

This commit is contained in:
Lightczx
2024-06-03 14:11:00 +08:00
parent 4bb7316ce5
commit ca66176d64
6 changed files with 93 additions and 89 deletions

View File

@@ -5,5 +5,10 @@ namespace Snap.Hutao.Core.LifeCycle.InterProcess.Model;
internal sealed class ElevationStatusResponse internal sealed class ElevationStatusResponse
{ {
public ElevationStatusResponse(bool isElevated)
{
IsElevated = isElevated;
}
public bool IsElevated { get; set; } public bool IsElevated { get; set; }
} }

View File

@@ -2,47 +2,83 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.ExceptionService;
using System.Buffers;
using System.IO.Hashing; using System.IO.Hashing;
using System.IO.Pipes; using System.IO.Pipes;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.LifeCycle.InterProcess; namespace Snap.Hutao.Core.LifeCycle.InterProcess;
internal static class PipeStreamExtension internal static class PipeStreamExtension
{ {
public static unsafe byte[] GetValidatedContent(this PipeStream stream, PipePacketHeader* header) public static TData? ReadJsonContent<TData>(this PipeStream stream, ref readonly PipePacketHeader header)
{ {
byte[] content = new byte[header->ContentLength]; using (IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent(header.ContentLength))
stream.ReadAtLeast(content, header->ContentLength, false); {
HutaoException.ThrowIf(XxHash64.HashToUInt64(content) != header->Checksum, "PipePacket Content Hash incorrect"); Span<byte> content = memoryOwner.Memory.Span[..header.ContentLength];
return content; stream.ReadExactly(content);
HutaoException.ThrowIf(XxHash64.HashToUInt64(content) != header.Checksum, "PipePacket Content Hash incorrect");
return JsonSerializer.Deserialize<TData>(content);
}
} }
public static unsafe PipePacketHeader ReadPacket<TData>(this PipeStream stream, out TData? data) public static unsafe void ReadPacket<TData>(this PipeStream stream, out PipePacketHeader header, out TData? data)
where TData : class where TData : class
{ {
data = default; data = default;
Span<byte> headerSpan = stackalloc byte[sizeof(PipePacketHeader)]; stream.ReadPacket(out header);
stream.ReadExactly(headerSpan); if (header.ContentType is PipePacketContentType.Json)
fixed (byte* pHeader = headerSpan)
{ {
PipePacketHeader* header = (PipePacketHeader*)pHeader; data = stream.ReadJsonContent<TData>(in header);
if (header->ContentType is PipePacketContentType.Json)
{
ReadOnlySpan<byte> content = stream.GetValidatedContent(header);
data = JsonSerializer.Deserialize<TData>(content);
}
return *header;
} }
} }
public static unsafe void WritePacket(this PipeStream stream, PipePacketHeader* header, byte[] content) [SkipLocalsInit]
public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header)
{ {
header->ContentLength = content.Length; fixed (PipePacketHeader* pHeader = &header)
header->Checksum = XxHash64.HashToUInt64(content); {
stream.ReadExactly(new(pHeader, sizeof(PipePacketHeader)));
}
}
stream.Write(new(header, sizeof(PipePacketHeader))); public static unsafe void WritePacketWithJsonContent<TData>(this PipeStream stream, byte version, PipePacketType type, PipePacketCommand command, TData data)
{
PipePacketHeader header = default;
header.Version = version;
header.Type = type;
header.Command = command;
header.ContentType = PipePacketContentType.Json;
stream.WritePacket(ref header, JsonSerializer.SerializeToUtf8Bytes(data));
}
public static unsafe void WritePacket(this PipeStream stream, ref PipePacketHeader header, byte[] content)
{
header.ContentLength = content.Length;
header.Checksum = XxHash64.HashToUInt64(content);
stream.WritePacket(in header);
stream.Write(content); stream.Write(content);
} }
public static unsafe void WritePacket(this PipeStream stream, byte version, PipePacketType type, PipePacketCommand command)
{
PipePacketHeader header = default;
header.Version = version;
header.Type = type;
header.Command = command;
stream.WritePacket(in header);
}
public static unsafe void WritePacket(this PipeStream stream, ref readonly PipePacketHeader header)
{
fixed (PipePacketHeader* pHeader = &header)
{
stream.Write(new(pHeader, sizeof(PipePacketHeader)));
}
}
} }

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
internal static class PrivateNamedPipe
{
public const int Version = 1;
public const string Name = "Snap.Hutao.PrivateNamedPipe";
}

View File

@@ -11,62 +11,30 @@ namespace Snap.Hutao.Core.LifeCycle.InterProcess;
[ConstructorGenerated] [ConstructorGenerated]
internal sealed partial class PrivateNamedPipeClient : IDisposable internal sealed partial class PrivateNamedPipeClient : IDisposable
{ {
private readonly NamedPipeClientStream clientStream = new(".", "Snap.Hutao.PrivateNamedPipe", PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough); private readonly NamedPipeClientStream clientStream = new(".", PrivateNamedPipe.Name, PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
private readonly RuntimeOptions runtimeOptions; private readonly RuntimeOptions runtimeOptions;
public unsafe bool TryRedirectActivationTo(AppActivationArguments args) public unsafe bool TryRedirectActivationTo(AppActivationArguments args)
{ {
if (clientStream.TryConnectOnce()) if (clientStream.TryConnectOnce())
{ {
clientStream.WritePacket(PrivateNamedPipe.Version, PipePacketType.Request, PipePacketCommand.RequestElevationStatus);
clientStream.ReadPacket(stackalloc byte[sizeof(PipePacketHeader)], out ElevationStatusResponse? response);
ArgumentNullException.ThrowIfNull(response);
// Prefer elevated instance
if (runtimeOptions.IsElevated && !response.IsElevated)
{ {
// Connect // Notify previous instance to exit
PipePacketHeader connectPacket = default; clientStream.WritePacket(PrivateNamedPipe.Version, PipePacketType.SessionTermination, PipePacketCommand.Exit);
connectPacket.Version = 1;
connectPacket.Type = PipePacketType.Request;
connectPacket.Command = PipePacketCommand.RequestElevationStatus;
clientStream.Write(new(&connectPacket, sizeof(PipePacketHeader)));
}
clientStream.ReadPacket(out ElevationStatusResponse? serverElevationStatus);
ArgumentNullException.ThrowIfNull(serverElevationStatus);
if (runtimeOptions.IsElevated && !serverElevationStatus.IsElevated)
{
// Kill previous instance to use current elevated instance
PipePacketHeader killPacket = default;
killPacket.Version = 1;
killPacket.Type = PipePacketType.SessionTermination;
killPacket.Command = PipePacketCommand.Exit;
clientStream.Write(new(&killPacket, sizeof(PipePacketHeader)));
clientStream.Flush(); clientStream.Flush();
return false; return false;
} }
{ // Redirect to previous instance
// Redirect to previous instance HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(args, isRedirected: true);
PipePacketHeader redirectActivationPacket = default; clientStream.WritePacketWithJsonContent(PrivateNamedPipe.Version, PipePacketType.Request, PipePacketCommand.RedirectActivation, hutaoArgs);
redirectActivationPacket.Version = 1; clientStream.WritePacket(PrivateNamedPipe.Version, PipePacketType.SessionTermination, PipePacketCommand.None);
redirectActivationPacket.Type = PipePacketType.Request;
redirectActivationPacket.Command = PipePacketCommand.RedirectActivation;
redirectActivationPacket.ContentType = PipePacketContentType.Json;
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(args, isRedirected: true);
byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(hutaoArgs);
clientStream.WritePacket(&redirectActivationPacket, jsonBytes);
}
{
// Terminate session
PipePacketHeader terminationPacket = default;
terminationPacket.Version = 1;
terminationPacket.Type = PipePacketType.SessionTermination;
clientStream.Write(new(&terminationPacket, sizeof(PipePacketHeader)));
}
clientStream.Flush(); clientStream.Flush();
return true; return true;
} }

View File

@@ -19,7 +19,7 @@ internal sealed partial class PrivateNamedPipeMessageDispatcher
serviceProvider.GetRequiredService<IAppActivation>().Activate(args); serviceProvider.GetRequiredService<IAppActivation>().Activate(args);
} }
public void Exit() public void ExitApplication()
{ {
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>(); ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
App app = serviceProvider.GetRequiredService<App>(); App app = serviceProvider.GetRequiredService<App>();

View File

@@ -35,7 +35,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
} }
serverStream = NamedPipeServerStreamAcl.Create( serverStream = NamedPipeServerStreamAcl.Create(
"Snap.Hutao.PrivateNamedPipe", PrivateNamedPipe.Name,
PipeDirection.InOut, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte, PipeTransmissionMode.Byte,
@@ -77,42 +77,27 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
{ {
while (serverStream.IsConnected && !token.IsCancellationRequested) while (serverStream.IsConnected && !token.IsCancellationRequested)
{ {
PipePacketHeader header = serverStream.ReadPacket(out HutaoActivationArguments? hutaoArgs); serverStream.ReadPacket(out PipePacketHeader header);
switch ((header.Type, header.Command)) switch ((header.Type, header.Command))
{ {
case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus): case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus):
RespondElevationStatus(); ElevationStatusResponse resp = new(runtimeOptions.IsElevated);
serverStream.WritePacketWithJsonContent(PrivateNamedPipe.Version, PipePacketType.Response, PipePacketCommand.ResponseElevationStatus, resp);
serverStream.Flush();
break; break;
case (PipePacketType.Request, PipePacketCommand.RedirectActivation): case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header);
messageDispatcher.RedirectActivation(hutaoArgs); messageDispatcher.RedirectActivation(hutaoArgs);
break; break;
case (PipePacketType.SessionTermination, _): case (PipePacketType.SessionTermination, _):
serverStream.Disconnect(); serverStream.Disconnect();
if (header.Command is PipePacketCommand.Exit) if (header.Command is PipePacketCommand.Exit)
{ {
messageDispatcher.Exit(); messageDispatcher.ExitApplication();
} }
return; return;
} }
} }
void RespondElevationStatus()
{
PipePacketHeader elevatedPacket = default;
elevatedPacket.Version = 1;
elevatedPacket.Type = PipePacketType.Response;
elevatedPacket.Command = PipePacketCommand.ResponseElevationStatus;
elevatedPacket.ContentType = PipePacketContentType.Json;
ElevationStatusResponse resp = new()
{
IsElevated = runtimeOptions.IsElevated,
};
byte[] elevatedBytes = JsonSerializer.SerializeToUtf8Bytes(resp);
serverStream.WritePacket(&elevatedPacket, elevatedBytes);
serverStream.Flush();
}
} }
} }