Exclude file-system based transfer from #1038 (#1064)

This commit is contained in:
Florian Rappl
2026-05-10 19:09:51 +02:00
committed by GitHub
parent fe939e0fda
commit 979b25ffe7
3 changed files with 28 additions and 149 deletions

View File

@@ -1,7 +1,6 @@
namespace ElectronNET.Common
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
@@ -112,11 +111,6 @@
public int? LastExitCode { get; private set; }
public bool Run(string exeFileName, string commandLineArgs, string workingDirectory)
{
return this.Run(exeFileName, commandLineArgs, workingDirectory, null);
}
public bool Run(string exeFileName, string commandLineArgs, string workingDirectory, IDictionary<string, string> environmentVariables)
{
this.CommandLine = commandLineArgs;
this.WorkingFolder = workingDirectory;
@@ -134,14 +128,6 @@
WorkingDirectory = workingDirectory
};
if (environmentVariables != null)
{
foreach (var kv in environmentVariables)
{
startInfo.EnvironmentVariables[kv.Key] = kv.Value;
}
}
return this.Run(startInfo);
}

View File

@@ -1,14 +1,11 @@
namespace ElectronNET.Runtime.Services.ElectronProcess
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using ElectronNET.Common;
@@ -20,8 +17,7 @@
[Localizable(false)]
internal class ElectronProcessActive : ElectronProcessBase
{
private const string AuthTokenEnvVar = "ELECTRONNET_AUTH_TOKEN";
private const string StartupInfoEnvVar = "ELECTRONNET_STARTUP_INFO";
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at .* using ([a-f0-9]+)$");
private readonly bool isUnpackaged;
private readonly string electronBinaryName;
@@ -96,23 +92,8 @@
workingDir = dir.FullName;
}
// Generate the auth token on the .NET side (256 bit entropy) and pass it
// to Electron via an environment variable. Electron will report the
// OS-selected port via a temporary handshake file - this avoids any
// dependency on parsing Electron's console output.
var authToken = CreateAuthToken();
var startupInfoPath = Path.Combine(
Path.GetTempPath(),
$"electronnet-startup-{Environment.ProcessId}-{Guid.NewGuid():N}.json");
// We don't await this in order to let the state transition to "Starting"
Task.Run(async () => await this.StartInternal(startCmd, args, workingDir, authToken, startupInfoPath).ConfigureAwait(false));
}
private static string CreateAuthToken()
{
var bytes = RandomNumberGenerator.GetBytes(32);
return Convert.ToHexString(bytes).ToLowerInvariant();
Task.Run(async () => await this.StartInternal(startCmd, args, workingDir).ConfigureAwait(false));
}
private void CheckRuntimeIdentifier()
@@ -177,7 +158,7 @@
return Task.CompletedTask;
}
private async Task StartInternal(string startCmd, string args, string directoriy, string authToken, string startupInfoPath)
private async Task StartInternal(string startCmd, string args, string directory)
{
var tcs = new TaskCompletionSource();
using var cts = new CancellationTokenSource(2 * 60_000); // cancel after 2 minutes
@@ -189,6 +170,23 @@
tcs.TrySetResult();
});
void Read_SocketIO_Parameters(object sender, string line)
{
// Look for "Electron Socket: listening on port %s at ..."
var match = extractor.Match(line);
if (match?.Success ?? false)
{
var port = int.Parse(match.Groups[1].Value);
var token = match.Groups[2].Value;
this.process.LineReceived -= Read_SocketIO_Parameters;
ElectronNetRuntime.ElectronAuthToken = token;
ElectronNetRuntime.ElectronSocketPort = port;
tcs.SetResult();
}
}
void Monitor_SocketIO_Failure(object sender, EventArgs e)
{
// We don't want to raise exceptions here - just pass the barrier
@@ -209,24 +207,10 @@
this.process = new ProcessRunner("ElectronRunner");
this.process.ProcessExited += Monitor_SocketIO_Failure;
this.process.LineReceived += Read_SocketIO_Parameters;
this.process.Run(startCmd, args, directoriy);
var env = new Dictionary<string, string>
{
[AuthTokenEnvVar] = authToken,
[StartupInfoEnvVar] = startupInfoPath,
};
this.process.Run(startCmd, args, directoriy, env);
// Wait for Electron to write the startup-info file (or for the process to die / timeout).
var waitTask = WaitForStartupInfoAsync(startupInfoPath, cts.Token);
var completed = await Task.WhenAny(waitTask, tcs.Task).ConfigureAwait(false);
int port = 0;
if (completed == waitTask && waitTask.Status == TaskStatus.RanToCompletion)
{
port = waitTask.Result;
}
await tcs.Task.ConfigureAwait(false);
Console.Error.WriteLine("[StartInternal]: after run:");
@@ -237,16 +221,9 @@
Task.Run(() => this.TransitionState(LifetimeState.Stopped));
}
else if (port > 0)
{
ElectronNetRuntime.ElectronAuthToken = authToken;
ElectronNetRuntime.ElectronSocketPort = port;
this.TransitionState(LifetimeState.Ready);
}
else
{
Console.Error.WriteLine("[StartInternal]: Did not receive Electron startup info before process exit/timeout.");
Task.Run(() => this.TransitionState(LifetimeState.Stopped));
this.TransitionState(LifetimeState.Ready);
}
}
catch (Exception ex)
@@ -256,63 +233,6 @@
Console.Error.WriteLine("[StartInternal]: Exception: " + ex);
throw;
}
finally
{
try
{
if (File.Exists(startupInfoPath))
{
File.Delete(startupInfoPath);
}
}
catch
{
// best effort cleanup
}
}
}
private static async Task<int> WaitForStartupInfoAsync(string startupInfoPath, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
if (File.Exists(startupInfoPath))
{
var json = await File.ReadAllTextAsync(startupInfoPath, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(json))
{
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("port", out var portElement) &&
portElement.TryGetInt32(out var port) &&
port > 0)
{
return port;
}
}
}
}
catch (JsonException)
{
// File may be partially written / racing with the writer - retry.
}
catch (IOException)
{
// Same - transient races on file access; retry.
}
try
{
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
break;
}
}
return 0;
}
private void Process_Exited(object sender, EventArgs e)