Ejemplo n.º 1
0
        /// <summary>
        /// Starts a child process as specified in <paramref name="startInfo"/>.
        /// </summary>
        /// <param name="startInfo"><see cref="ChildProcessStartInfo"/>.</param>
        /// <returns>The started process.</returns>
        /// <exception cref="ArgumentException"><paramref name="startInfo"/> has an invalid value.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="startInfo"/> is null.</exception>
        /// <exception cref="FileNotFoundException">The executable not found.</exception>
        /// <exception cref="IOException">Failed to open a specified file.</exception>
        /// <exception cref="AsmichiChildProcessLibraryCrashedException">The operation failed due to critical disturbance.</exception>
        /// <exception cref="Win32Exception">Another kind of native errors.</exception>
        public static IChildProcess Start(ChildProcessStartInfo startInfo)
        {
            _ = startInfo ?? throw new ArgumentNullException(nameof(startInfo));

            var startInfoInternal = new ChildProcessStartInfoInternal(startInfo);

            _ = startInfoInternal.FileName ?? throw new ArgumentException("ChildProcessStartInfo.FileName must not be null.", nameof(startInfo));
            _ = startInfoInternal.Arguments ?? throw new ArgumentException("ChildProcessStartInfo.Arguments must not be null.", nameof(startInfo));

            var flags = startInfoInternal.Flags;

            if (flags.HasUseCustomCodePage() && !flags.HasCreateNewConsole())
            {
                throw new ArgumentException(
                          $"{nameof(ChildProcessFlags.UseCustomCodePage)} requires {nameof(ChildProcessFlags.CreateNewConsole)}.", nameof(startInfo));
            }

            var resolvedPath = ResolveExecutablePath(startInfoInternal.FileName, startInfoInternal.Flags);

            using var stdHandles = new PipelineStdHandleCreator(ref startInfoInternal);
            IChildProcessStateHolder processState;

            try
            {
                processState = ChildProcessContext.Shared.SpawnProcess(
                    startInfo: ref startInfoInternal,
                    resolvedPath: resolvedPath,
                    stdIn: stdHandles.PipelineStdIn,
                    stdOut: stdHandles.PipelineStdOut,
                    stdErr: stdHandles.PipelineStdErr);
            }
            catch (Win32Exception ex)
            {
                if (EnvironmentPal.IsFileNotFoundError(ex.NativeErrorCode))
                {
                    ThrowHelper.ThrowExecutableNotFoundException(resolvedPath, startInfoInternal.Flags, ex);
                }

                // Win32Exception does not provide detailed information by its type.
                // The NativeErrorCode and Message property should be enough because normally there is
                // nothing we can do to programmatically recover from this error.
                throw;
            }

            var process = new ChildProcessImpl(processState, stdHandles.InputStream, stdHandles.OutputStream, stdHandles.ErrorStream);

            stdHandles.DetachStreams();
            return(process);
        }
        public unsafe IChildProcessStateHolder SpawnProcess(
            ref ChildProcessStartInfoInternal startInfo,
            string resolvedPath,
            SafeHandle stdIn,
            SafeHandle stdOut,
            SafeHandle stdErr)
        {
            var arguments            = startInfo.Arguments;
            var environmentVariables = startInfo.EnvironmentVariables;
            var workingDirectory     = startInfo.WorkingDirectory;
            var flags = startInfo.Flags;

            Debug.Assert(startInfo.CreateNewConsole || ConsolePal.HasConsoleWindow());

            var commandLine      = WindowsCommandLineUtil.MakeCommandLine(resolvedPath, arguments ?? Array.Empty <string>(), !flags.HasDisableArgumentQuoting());
            var environmentBlock = environmentVariables != null?WindowsEnvironmentBlockUtil.MakeEnvironmentBlock(environmentVariables) : null;

            var pseudoConsole = startInfo.CreateNewConsole ? InputWriterOnlyPseudoConsole.Create() : null;

            try
            {
                if (pseudoConsole is { } && flags.HasUseCustomCodePage())
                {
                    ChangeCodePage(pseudoConsole, startInfo.CodePage, workingDirectory);
                }

                using var inheritableHandleStore = new InheritableHandleStore(3);
                var childStdIn = stdIn != null?inheritableHandleStore.Add(stdIn) : null;

                var childStdOut = stdOut != null?inheritableHandleStore.Add(stdOut) : null;

                var childStdErr = stdErr != null?inheritableHandleStore.Add(stdErr) : null;

                Span <IntPtr> inheritableHandles = stackalloc IntPtr[inheritableHandleStore.Count];
                inheritableHandleStore.DangerousGetHandles(inheritableHandles);
                fixed(IntPtr *pInheritableHandles = inheritableHandles)
                {
                    using var attr = new ProcThreadAttributeList(2);
                    if (pseudoConsole is { })
Ejemplo n.º 3
0
        public IChildProcessStateHolder SpawnProcess(
            ref ChildProcessStartInfoInternal startInfo,
            string resolvedPath,
            SafeHandle stdIn,
            SafeHandle stdOut,
            SafeHandle stdErr)
        {
            var arguments            = startInfo.Arguments;
            var environmentVariables = startInfo.EnvironmentVariables;
            var workingDirectory     = startInfo.WorkingDirectory;

            Span <int> fds         = stackalloc int[3];
            int        handleCount = 0;
            uint       flags       = 0;

            if (stdIn != null)
            {
                fds[handleCount++] = stdIn.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStdin;
            }
            if (stdOut != null)
            {
                fds[handleCount++] = stdOut.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStdout;
            }
            if (stdErr != null)
            {
                fds[handleCount++] = stdErr.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStderr;
            }

            using var bw = new MyBinaryWriter(InitialBufferCapacity);
            var stateHolder = UnixChildProcessState.Create();

            try
            {
                bw.Write(0U); // Dummy length to be rewritten later
                bw.Write(stateHolder.State.Token);
                bw.Write(flags);
                bw.Write(workingDirectory);
                bw.Write(resolvedPath);

                bw.Write((uint)(arguments.Count + 1));
                bw.Write(resolvedPath);
                foreach (var x in arguments)
                {
                    bw.Write(x);
                }

                if (environmentVariables == null)
                {
                    bw.Write(0U);
                }
                else
                {
                    bw.Write((uint)environmentVariables.Count);
                    foreach (var(name, value) in environmentVariables)
                    {
                        bw.WriteEnvironmentVariable(name, value);
                    }
                }

                bw.RewritePrefixedLength();

                var subchannel = _helperProcess.RentSubchannelAsync(default).AsTask().GetAwaiter().GetResult();
        public PipelineStdHandleCreator(ref ChildProcessStartInfoInternal startInfo)
        {
            var stdInputRedirection  = startInfo.StdInputRedirection;
            var stdOutputRedirection = startInfo.StdOutputRedirection;
            var stdErrorRedirection  = startInfo.StdErrorRedirection;
            var stdInputFile         = startInfo.StdInputFile;
            var stdOutputFile        = startInfo.StdOutputFile;
            var stdErrorFile         = startInfo.StdErrorFile;
            var stdInputHandle       = startInfo.StdInputHandle;
            var stdOutputHandle      = startInfo.StdOutputHandle;
            var stdErrorHandle       = startInfo.StdErrorHandle;

            if (stdInputRedirection == InputRedirection.Handle && stdInputHandle == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdInputHandle)} must not be null.", nameof(startInfo));
            }
            if (stdInputRedirection == InputRedirection.File && stdInputFile == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdInputFile)} must not be null.", nameof(startInfo));
            }
            if (stdOutputRedirection == OutputRedirection.Handle && stdOutputHandle == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdOutputHandle)} must not be null.", nameof(startInfo));
            }
            if (IsFileRedirection(stdOutputRedirection) && stdOutputFile == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdOutputFile)} must not be null.", nameof(startInfo));
            }
            if (stdErrorRedirection == OutputRedirection.Handle && stdErrorHandle == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdErrorHandle)} must not be null.", nameof(startInfo));
            }
            if (IsFileRedirection(stdErrorRedirection) && stdErrorFile == null)
            {
                throw new ArgumentException($"{nameof(ChildProcessStartInfo.StdErrorFile)} must not be null.", nameof(startInfo));
            }

            bool redirectingToSameFile = IsFileRedirection(stdOutputRedirection) && IsFileRedirection(stdErrorRedirection) && stdOutputFile == stdErrorFile;

            if (redirectingToSameFile && stdErrorRedirection != stdOutputRedirection)
            {
                throw new ArgumentException(
                          "StdOutputRedirection and StdErrorRedirection must be the same value when both stdout and stderr redirect to the same file.",
                          nameof(startInfo));
            }

            try
            {
                if (stdInputRedirection == InputRedirection.InputPipe)
                {
                    (InputStream, _inputReadPipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.Out);
                }

                if (stdOutputRedirection == OutputRedirection.OutputPipe ||
                    stdErrorRedirection == OutputRedirection.OutputPipe)
                {
                    (OutputStream, _outputWritePipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.In);
                }

                if (stdOutputRedirection == OutputRedirection.ErrorPipe ||
                    stdErrorRedirection == OutputRedirection.ErrorPipe)
                {
                    (ErrorStream, _errorWritePipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.In);
                }

                PipelineStdIn = ChooseInput(
                    stdInputRedirection,
                    stdInputFile,
                    stdInputHandle,
                    _inputReadPipe,
                    startInfo.CreateNewConsole);

                PipelineStdOut = ChooseOutput(
                    stdOutputRedirection,
                    stdOutputFile,
                    stdOutputHandle,
                    _outputWritePipe,
                    _errorWritePipe,
                    startInfo.CreateNewConsole);

                if (redirectingToSameFile)
                {
                    PipelineStdErr = PipelineStdOut;
                }
                else
                {
                    PipelineStdErr = ChooseOutput(
                        stdErrorRedirection,
                        stdErrorFile,
                        stdErrorHandle,
                        _outputWritePipe,
                        _errorWritePipe,
                        startInfo.CreateNewConsole);
                }
            }
            catch
            {
                Dispose();
                throw;
            }
        }