File: System\Runtime\InteropServices\UnixProcessStartArguments.cs
Web Access
Project: src\runtime\src\libraries\System.Diagnostics.Process\src\System.Diagnostics.Process.csproj (System.Diagnostics.Process)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.Versioning;

namespace System.Runtime.InteropServices
{
    /// <summary>
    /// Provides the prepared data required to start a process via a user-supplied callback.
    /// This ref struct is populated by the <see cref="Start(ProcessStartInfo, Func{UnixProcessStartArguments, int})"/> method.
    /// </summary>
    public readonly ref struct UnixProcessStartArguments
    {
        internal unsafe UnixProcessStartArguments(byte* resolvedPath, byte** arguments, byte** environmentVariables, nint standardInput, nint standardOutput, nint standardError, ProcessStartInfo processStartInfo)
        {
            ResolvedPath = resolvedPath;
            Arguments = arguments;
            EnvironmentVariables = environmentVariables;
            StandardInput = standardInput;
            StandardOutput = standardOutput;
            StandardError = standardError;
            ProcessStartInfo = processStartInfo;
        }

        /// <summary>
        /// Gets a pointer to the resolved executable path encoded as null-terminated UTF-8.
        /// </summary>
        /// <value>
        /// A pointer to a null-terminated UTF-8 encoded string representing the resolved executable path.
        /// </value>
        /// <remarks>
        /// The memory pointed to by this property is only valid for the duration of the callback invocation.
        /// Do not cache or use this pointer after the callback returns.
        /// </remarks>
        [CLSCompliant(false)]
        public unsafe byte* ResolvedPath { get; }

        /// <summary>
        /// Gets a pointer to the command-line arguments for the process.
        /// This is a pointer to a null-terminated array of pointers to null-terminated UTF-8 byte strings (argv).
        /// </summary>
        /// <remarks>
        /// The memory pointed to by this property is only valid for the duration of the callback invocation.
        /// </remarks>
        [CLSCompliant(false)]
        public unsafe byte** Arguments { get; }

        /// <summary>
        /// Gets a pointer to the environment variables block for the new process.
        /// This is a pointer to a null-terminated array of pointers to null-terminated UTF-8 byte strings ("name=value").
        /// </summary>
        /// <remarks>
        /// The memory pointed to by this property is only valid for the duration of the callback invocation.
        /// </remarks>
        [CLSCompliant(false)]
        public unsafe byte** EnvironmentVariables { get; }

        /// <summary>
        /// Gets the raw handle to use as the standard input for the new process.
        /// </summary>
        public nint StandardInput { get; }

        /// <summary>
        /// Gets the raw handle to use as the standard output for the new process.
        /// </summary>
        public nint StandardOutput { get; }

        /// <summary>
        /// Gets the raw handle to use as the standard error for the new process.
        /// </summary>
        public nint StandardError { get; }

        /// <summary>
        /// Gets the original <see cref="ProcessStartInfo"/> provided by the user,
        /// allowing the callback to inspect any additional configuration.
        /// </summary>
        public ProcessStartInfo ProcessStartInfo { get; }

        /// <summary>
        /// Starts a new process by preparing all necessary arguments (standard handles, command line, environment)
        /// and then invoking the user-supplied <paramref name="callback"/> to perform the actual process creation system call.
        /// The callback receives a <see cref="UnixProcessStartArguments"/> instance with the prepared data and must return the
        /// process ID of the newly created process.
        /// </summary>
        /// <param name="startInfo">The <see cref="ProcessStartInfo"/> that contains the information used to start the process.</param>
        /// <param name="callback">
        /// A function that receives the prepared <see cref="UnixProcessStartArguments"/> and creates the process using any system call of the user's choice.
        /// The callback must return a positive process ID for the newly created process.
        /// The memory referenced by pointer properties in <see cref="UnixProcessStartArguments"/> is only valid for the duration of the callback.
        /// The callback is invoked while an internal process-start lock is held; calling System.Diagnostics.Process APIs that start processes from within the callback may deadlock or throw.
        /// </param>
        /// <returns>A new <see cref="Process"/> instance associated with the started process.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="startInfo"/> or <paramref name="callback"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException">The process ID returned by the callback is not positive.</exception>
        /// <exception cref="InvalidOperationException"><see cref="ProcessStartInfo.UseShellExecute"/> is set to <see langword="true"/>.</exception>
        [UnsupportedOSPlatform("windows")]
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [SupportedOSPlatform("maccatalyst")]
        public static Process Start(ProcessStartInfo startInfo, Func<UnixProcessStartArguments, int> callback)
        {
#if TARGET_WINDOWS
            throw new PlatformNotSupportedException();
#else
            ArgumentNullException.ThrowIfNull(startInfo);
            ArgumentNullException.ThrowIfNull(callback);

            if (startInfo.UseShellExecute)
            {
                throw new InvalidOperationException(SR.Format(SR.UseShellExecuteNotSupportedForScenario, nameof(Start)));
            }

            if (!ProcessUtils.PlatformSupportsProcessStartAndKill)
            {
                throw new PlatformNotSupportedException();
            }

            Process process = new();

            try
            {
                process.StartCoreWithCallback(startInfo, callback);
            }
            catch
            {
                process.Dispose();

                throw;
            }

            return process;
#endif
        }
    }
}