xenoinc/ToolsHub

VeraCrypt loader to be async

Opened this issue · 0 comments

As a user, I want the ProcessStart execution to be async.

This would assist the VeraCrypt driver loader in VeraCrypt/Drive.cs and VeraCryptAddin/Domain/VirtualDrive.cs.

Sample Start Process Async

Reference: RoslynPad

Usage

var buildArgs =
  $"-nologo -v:q -p:Configuration={optimizationLevel} -p:AssemblyName={Name} " +
  $"-bl:ProjectImports=None \"{csprojPath}\" ";

using var buildResult = await ProcessUtil.RunProcessAsync(
  DotNetExecutable,
  BuildPath,
  $"build {buildArgs}",
  cancellationToken).ConfigureAwait(false);

await buildResult.WaitForExitAsync().ConfigureAwait(false);

ProcessUtil

internal class ProcessUtil
{
  public static async Task<ProcessResult> RunProcessAsync(string path, string workingDirectory, string arguments, CancellationToken cancellationToken)
  {
    var process = new Process
    {
      StartInfo = new ProcessStartInfo
      {
        FileName = path,
        WorkingDirectory = workingDirectory,
        Arguments = arguments,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        CreateNoWindow = true,
        UseShellExecute = false,
      },
      EnableRaisingEvents = true,
    };

    var exitTcs = new TaskCompletionSource<object?>();
    process.Exited += (_, _) => exitTcs.TrySetResult(null);

    using var _ = cancellationToken.Register(() =>
    {
      try
      {
        exitTcs.TrySetCanceled();
        process.Kill();
      }
      catch { }
    });

    await Task.Run(process.Start).ConfigureAwait(false);

    return new ProcessResult(process, exitTcs);
  }

  public class ProcessResult : IDisposable
  {
    private readonly Process _process;
    private readonly TaskCompletionSource<object?> _exitTcs;
    private readonly StringBuilder _standardOutput;

    internal ProcessResult(Process process, TaskCompletionSource<object?> exitTcs)
    {
      _process = process;
      _exitTcs = exitTcs;
      _standardOutput = new StringBuilder();

      _ = Task.Run(ReadStandardErrorAsync);
    }

    private async Task ReadStandardErrorAsync() =>
      StandardError = await _process.StandardError.ReadToEndAsync().ConfigureAwait(false);

    public Task WaitForExitAsync() => _exitTcs.Task;

    public async IAsyncEnumerable<string> GetStandardOutputLinesAsync()
    {
      var output = _process.StandardOutput;
      while (true)
      {
        var line = await output.ReadLineAsync().ConfigureAwait(false);
        if (line == null)
        {
          await _exitTcs.Task.ConfigureAwait(false);
          yield break;
        }

        if (!string.IsNullOrWhiteSpace(line))
        {
          _standardOutput.AppendLine(line);
          yield return line;
        }
      }
    }

    public int ExitCode => _process.ExitCode;

    public string StandardOutput => _standardOutput.ToString();
    public string? StandardError { get; private set; }

    public void Dispose() => _process.Dispose();
  }
}