ProcessStartInfo hanging on “WaitForExit”? Why?

前端 未结 22 2158
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-22 02:59

I have the following code:

info = new System.Diagnostics.ProcessStartInfo(\"TheProgram.exe\", String.Join(\" \", args));
info.CreateNoWindow = true;
info.Win         


        
22条回答
  •  滥情空心
    2020-11-22 03:16

    Introduction

    Currently accepted answer doesn't work (throws exception) and there are too many workarounds but no complete code. This is obviously wasting lots of people's time because this is a popular question.

    Combining Mark Byers' answer and Karol Tyl's answer I wrote full code based on how I want to use the Process.Start method.

    Usage

    I have used it to create progress dialog around git commands. This is how I've used it:

        private bool Run(string fullCommand)
        {
            Error = "";
            int timeout = 5000;
    
            var result = ProcessNoBS.Start(
                filename: @"C:\Program Files\Git\cmd\git.exe",
                arguments: fullCommand,
                timeoutInMs: timeout,
                workingDir: @"C:\test");
    
            if (result.hasTimedOut)
            {
                Error = String.Format("Timeout ({0} sec)", timeout/1000);
                return false;
            }
    
            if (result.ExitCode != 0)
            {
                Error = (String.IsNullOrWhiteSpace(result.stderr)) 
                    ? result.stdout : result.stderr;
                return false;
            }
    
            return true;
        }
    

    In theory you can also combine stdout and stderr, but I haven't tested that.

    Code

    public struct ProcessResult
    {
        public string stdout;
        public string stderr;
        public bool hasTimedOut;
        private int? exitCode;
    
        public ProcessResult(bool hasTimedOut = true)
        {
            this.hasTimedOut = hasTimedOut;
            stdout = null;
            stderr = null;
            exitCode = null;
        }
    
        public int ExitCode
        {
            get 
            {
                if (hasTimedOut)
                    throw new InvalidOperationException(
                        "There was no exit code - process has timed out.");
    
                return (int)exitCode;
            }
            set
            {
                exitCode = value;
            }
        }
    }
    
    public class ProcessNoBS
    {
        public static ProcessResult Start(string filename, string arguments,
            string workingDir = null, int timeoutInMs = 5000,
            bool combineStdoutAndStderr = false)
        {
            using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
            using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
            {
                using (var process = new Process())
                {
                    var info = new ProcessStartInfo();
    
                    info.CreateNoWindow = true;
                    info.FileName = filename;
                    info.Arguments = arguments;
                    info.UseShellExecute = false;
                    info.RedirectStandardOutput = true;
                    info.RedirectStandardError = true;
    
                    if (workingDir != null)
                        info.WorkingDirectory = workingDir;
    
                    process.StartInfo = info;
    
                    StringBuilder stdout = new StringBuilder();
                    StringBuilder stderr = combineStdoutAndStderr
                        ? stdout : new StringBuilder();
    
                    var result = new ProcessResult();
    
                    try
                    {
                        process.OutputDataReceived += (sender, e) =>
                        {
                            if (e.Data == null)
                                outputWaitHandle.Set();
                            else
                                stdout.AppendLine(e.Data);
                        };
                        process.ErrorDataReceived += (sender, e) =>
                        {
                            if (e.Data == null)
                                errorWaitHandle.Set();
                            else
                                stderr.AppendLine(e.Data);
                        };
    
                        process.Start();
    
                        process.BeginOutputReadLine();
                        process.BeginErrorReadLine();
    
                        if (process.WaitForExit(timeoutInMs))
                            result.ExitCode = process.ExitCode;
                        // else process has timed out 
                        // but that's already default ProcessResult
    
                        result.stdout = stdout.ToString();
                        if (combineStdoutAndStderr)
                            result.stderr = null;
                        else
                            result.stderr = stderr.ToString();
    
                        return result;
                    }
                    finally
                    {
                        outputWaitHandle.WaitOne(timeoutInMs);
                        errorWaitHandle.WaitOne(timeoutInMs);
                    }
                }
            }
        }
    }
    

提交回复
热议问题