use client to determine whether to redirect

This commit is contained in:
qhy040404
2024-05-31 23:30:25 +08:00
parent 72e163f613
commit ea345f4854
6 changed files with 71 additions and 43 deletions

View File

@@ -8,8 +8,6 @@ namespace Snap.Hutao.Core.LifeCycle;
internal sealed class HutaoActivationArguments
{
public bool IsElevated { get; set; }
public bool IsRedirectTo { get; set; }
public bool IsToastActivated { get; set; }
@@ -20,11 +18,10 @@ internal sealed class HutaoActivationArguments
public string? LaunchActivatedArguments { get; set; }
public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false, bool isElevated = false)
public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false)
{
HutaoActivationArguments result = new()
{
IsElevated = isElevated,
IsRedirectTo = isRedirected,
};

View File

@@ -6,6 +6,9 @@ namespace Snap.Hutao.Core.LifeCycle.InterProcess;
internal enum PipePacketCommand : byte
{
None = 0,
Connect = 1,
RedirectActivation = 10,
Exit = 30,
}

View File

@@ -8,5 +8,5 @@ internal enum PipePacketType : byte
None = 0,
Request = 1,
Response = 2,
Termination = 3,
SessionTermination = 3,
}

View File

@@ -17,41 +17,67 @@ internal sealed partial class PrivateNamedPipeClient : IDisposable
{
if (clientStream.TryConnectOnce())
{
bool shouldElevate = false;
Span<byte> headerSpan = stackalloc byte[sizeof(PipePacketHeader)];
bool serverElevated = false;
{
// Connect
PipePacketHeader connectPacket = default;
connectPacket.Version = 1;
connectPacket.Type = PipePacketType.Request;
connectPacket.Command = PipePacketCommand.Connect;
clientStream.Write(new(&connectPacket, sizeof(PipePacketHeader)));
}
{
// Get previous instance elevated status
clientStream.ReadExactly(headerSpan);
fixed (byte* pHeader = headerSpan)
{
PipePacketHeader* header = (PipePacketHeader*)pHeader;
ReadOnlySpan<byte> content = clientStream.GetValidatedContent(header);
serverElevated = JsonSerializer.Deserialize<bool>(content);
}
}
if (!serverElevated && runtimeOptions.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();
return false;
}
{
// Redirect to previous instance
PipePacketHeader redirectActivationPacket = default;
redirectActivationPacket.Version = 1;
redirectActivationPacket.Type = PipePacketType.Request;
redirectActivationPacket.Command = PipePacketCommand.RedirectActivation;
redirectActivationPacket.ContentType = PipePacketContentType.Json;
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(args, isRedirected: true, isElevated: runtimeOptions.IsElevated);
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(args, isRedirected: true);
byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(hutaoArgs);
clientStream.WritePacket(&redirectActivationPacket, jsonBytes);
}
{
Span<byte> headerSpan = stackalloc byte[sizeof(PipePacketHeader)];
clientStream.ReadExactly(headerSpan);
fixed (byte* pHeader = headerSpan)
{
PipePacketHeader* header = (PipePacketHeader*)pHeader;
ReadOnlySpan<byte> content = clientStream.GetValidatedContent(header);
shouldElevate = JsonSerializer.Deserialize<bool>(content);
}
}
{
// Terminate session
PipePacketHeader terminationPacket = default;
terminationPacket.Version = 1;
terminationPacket.Type = PipePacketType.Termination;
terminationPacket.Type = PipePacketType.SessionTermination;
clientStream.Write(new(&terminationPacket, sizeof(PipePacketHeader)));
}
clientStream.Flush();
return !shouldElevate;
return true;
}
return false;

View File

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

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Diagnostics;
using System.IO.Pipes;
using System.Security.AccessControl;
using System.Security.Principal;
@@ -13,6 +12,8 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
{
private readonly PrivateNamedPipeMessageDispatcher messageDispatcher;
private readonly RuntimeOptions runtimeOptions;
private readonly ITaskContext taskContext;
private readonly App app;
private readonly CancellationTokenSource serverTokenSource = new();
private readonly SemaphoreSlim serverSemaphore = new(1);
@@ -23,6 +24,8 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
{
messageDispatcher = serviceProvider.GetRequiredService<PrivateNamedPipeMessageDispatcher>();
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
app = serviceProvider.GetRequiredService<App>();
PipeSecurity? pipeSecurity = default;
@@ -76,7 +79,6 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
private unsafe void RunPacketSession(NamedPipeServerStream serverStream, CancellationToken token)
{
Span<byte> headerSpan = stackalloc byte[sizeof(PipePacketHeader)];
bool shouldElevate = false;
bool sessionTerminated = false;
while (serverStream.IsConnected && !sessionTerminated && !token.IsCancellationRequested)
{
@@ -87,33 +89,26 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
switch ((header->Type, header->Command, header->ContentType))
{
case (PipePacketType.Request, PipePacketCommand.RedirectActivation, PipePacketContentType.Json):
ReadOnlySpan<byte> content = serverStream.GetValidatedContent(header);
HutaoActivationArguments? hutaoArgs = JsonSerializer.Deserialize<HutaoActivationArguments>(content);
ArgumentNullException.ThrowIfNull(hutaoArgs);
case (PipePacketType.Request, PipePacketCommand.Connect, _):
PipePacketHeader elevatedPacket = default;
elevatedPacket.Version = 1;
elevatedPacket.Type = PipePacketType.Response;
elevatedPacket.ContentType = PipePacketContentType.Json;
PipePacketHeader responsePacket = default;
responsePacket.Version = 1;
responsePacket.Type = PipePacketType.Response;
responsePacket.ContentType = PipePacketContentType.Json;
shouldElevate = !runtimeOptions.IsElevated && hutaoArgs.IsElevated;
byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(shouldElevate);
serverStream.WritePacket(&responsePacket, jsonBytes);
serverStream.Flush();
if (!shouldElevate)
{
messageDispatcher.RedirectActivation(hutaoArgs);
}
byte[] elevatedBytes = JsonSerializer.SerializeToUtf8Bytes(runtimeOptions.IsElevated);
serverStream.WritePacket(&elevatedPacket, elevatedBytes);
break;
case (PipePacketType.Termination, _, _):
case (PipePacketType.Request, PipePacketCommand.RedirectActivation, PipePacketContentType.Json):
ReadOnlySpan<byte> content = serverStream.GetValidatedContent(header);
messageDispatcher.RedirectActivation(JsonSerializer.Deserialize<HutaoActivationArguments>(content));
break;
case (PipePacketType.SessionTermination, _, _):
serverStream.Disconnect();
sessionTerminated = true;
if (shouldElevate)
if (header->Command is PipePacketCommand.Exit)
{
Process.GetCurrentProcess().Kill();
messageDispatcher.Exit();
}
return;