Pergunta

Eu tenho o seguinte código:

info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents

Eu sei que a saída do processo que eu estou começando é de cerca de 7MB longa. Executá-lo no console do Windows fina funciona. Infelizmente programaticamente este trava indefinidamente no WaitForExit. Note-se também este Código não pendurar para saídas menores (como 3 KB).

É possível que o StandardOutput interna em ProcessStartInfo não pode buffer 7MB? Se assim for, o que devo fazer em vez disso? Se não, o que estou fazendo de errado?

Foi útil?

Solução

O problema é que se você redirecionar StandardOutput e / ou StandardError o buffer interno pode se tornar completo. Seja qual for a ordem que você usar, pode haver um problema:

  • Se você aguardar o processo para sair antes de ler StandardOutput o processo pode bloquear tentando escrever a ele, de modo que o processo nunca termina.
  • Se você ler a partir StandardOutput usando ReadToEnd então o processo pode bloquear se o processo nunca fecha StandardOutput (por exemplo, se ele nunca termina, ou, se for bloqueado escrita para StandardError).

A solução é usar assíncrono lê para garantir que o tampão não fica completo. Para evitar impasses e recolher-se todas as saídas de ambos StandardOutput e StandardError você pode fazer isso:

EDIT:. Veja as respostas abaixo para saber como evitar uma ObjectDisposedException se o tempo limite ocorre

using (Process process = new Process())
{
    process.StartInfo.FileName = filename;
    process.StartInfo.Arguments = arguments;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;

    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();

    using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
    using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
    {
        process.OutputDataReceived += (sender, e) => {
            if (e.Data == null)
            {
                outputWaitHandle.Set();
            }
            else
            {
                output.AppendLine(e.Data);
            }
        };
        process.ErrorDataReceived += (sender, e) =>
        {
            if (e.Data == null)
            {
                errorWaitHandle.Set();
            }
            else
            {
                error.AppendLine(e.Data);
            }
        };

        process.Start();

        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        if (process.WaitForExit(timeout) &&
            outputWaitHandle.WaitOne(timeout) &&
            errorWaitHandle.WaitOne(timeout))
        {
            // Process completed. Check process.ExitCode here.
        }
        else
        {
            // Timed out.
        }
    }
}

Outras dicas

O documentação para Process.StandardOutput diz leia antes de esperar caso contrário você pode impasse, snippet copiado abaixo:

 // Start the child process.
 Process p = new Process();
 // Redirect the output stream of the child process.
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = "Write500Lines.exe";
 p.Start();
 // Do not wait for the child process to exit before
 // reading to the end of its redirected stream.
 // p.WaitForExit();
 // Read the output stream first and then wait.
 string output = p.StandardOutput.ReadToEnd();
 p.WaitForExit();

resposta Mark Byers' é excelente, mas gostaria apenas de acrescentar o seguinte: os delegados OutputDataReceived e ErrorDataReceived precisam ser removidos antes da outputWaitHandle e errorWaitHandle get descartado. Se o processo continua para os dados de saída, após o limite de tempo foi excedido e, em seguida, termina, as variáveis ??outputWaitHandle e errorWaitHandle será acedida depois de ter sido descartado.

(FYI eu tinha para adicionar esta ressalva como uma resposta que pude não comentário em seu post.)

O problema com ObjectDisposedException não tratada acontece quando o processo é excedido. Nesse caso, as outras partes da condição:

if (process.WaitForExit(timeout) 
    && outputWaitHandle.WaitOne(timeout) 
    && errorWaitHandle.WaitOne(timeout))

não são executados. Eu resolvi este problema de uma maneira seguinte:

using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
    using (Process process = new Process())
    {
        // preparing ProcessStartInfo

        try
        {
            process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        outputBuilder.AppendLine(e.Data);
                    }
                };
            process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        errorBuilder.AppendLine(e.Data);
                    }
                };

            process.Start();

            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            if (process.WaitForExit(timeout))
            {
                exitCode = process.ExitCode;
            }
            else
            {
                // timed out
            }

            output = outputBuilder.ToString();
        }
        finally
        {
            outputWaitHandle.WaitOne(timeout);
            errorWaitHandle.WaitOne(timeout);
        }
    }
}

Esta é uma biblioteca paralela de tarefas (TPL) solução mais moderna awaitable, com base em .NET 4.5 e acima.

Exemplo de Utilização

try
{
    var exitCode = await StartProcess(
        "dotnet", 
        "--version", 
        @"C:\",
        10000, 
        Console.Out, 
        Console.Out);
    Console.WriteLine($"Process Exited with Exit Code {exitCode}!");
}
catch (TaskCanceledException)
{
    Console.WriteLine("Process Timed Out!");
}

Implementação

public static async Task<int> StartProcess(
    string filename,
    string arguments,
    string workingDirectory= null,
    int? timeout = null,
    TextWriter outputTextWriter = null,
    TextWriter errorTextWriter = null)
{
    using (var process = new Process()
    {
        StartInfo = new ProcessStartInfo()
        {
            CreateNoWindow = true,
            Arguments = arguments,
            FileName = filename,
            RedirectStandardOutput = outputTextWriter != null,
            RedirectStandardError = errorTextWriter != null,
            UseShellExecute = false,
            WorkingDirectory = workingDirectory
        }
    })
    {
        process.Start();
        var cancellationTokenSource = timeout.HasValue ?
            new CancellationTokenSource(timeout.Value) :
            new CancellationTokenSource();

        var tasks = new List<Task>(3) { process.WaitForExitAsync(cancellationTokenSource.Token) };
        if (outputTextWriter != null)
        {
            tasks.Add(ReadAsync(
                x =>
                {
                    process.OutputDataReceived += x;
                    process.BeginOutputReadLine();
                },
                x => process.OutputDataReceived -= x,
                outputTextWriter,
                cancellationTokenSource.Token));
        }

        if (errorTextWriter != null)
        {
            tasks.Add(ReadAsync(
                x =>
                {
                    process.ErrorDataReceived += x;
                    process.BeginErrorReadLine();
                },
                x => process.ErrorDataReceived -= x,
                errorTextWriter,
                cancellationTokenSource.Token));
        }

        await Task.WhenAll(tasks);
        return process.ExitCode;
    }
}

/// <summary>
/// Waits asynchronously for the process to exit.
/// </summary>
/// <param name="process">The process to wait for cancellation.</param>
/// <param name="cancellationToken">A cancellation token. If invoked, the task will return
/// immediately as cancelled.</param>
/// <returns>A Task representing waiting for the process to end.</returns>
public static Task WaitForExitAsync(
    this Process process,
    CancellationToken cancellationToken = default(CancellationToken))
{
    process.EnableRaisingEvents = true;

    var taskCompletionSource = new TaskCompletionSource<object>();

    EventHandler handler = null;
    handler = (sender, args) =>
    {
        process.Exited -= handler;
        taskCompletionSource.TrySetResult(null);
    };
    process.Exited += handler;

    if (cancellationToken != default(CancellationToken))
    {
        cancellationToken.Register(
            () =>
            {
                process.Exited -= handler;
                taskCompletionSource.TrySetCanceled();
            });
    }

    return taskCompletionSource.Task;
}

/// <summary>
/// Reads the data from the specified data recieved event and writes it to the
/// <paramref name="textWriter"/>.
/// </summary>
/// <param name="addHandler">Adds the event handler.</param>
/// <param name="removeHandler">Removes the event handler.</param>
/// <param name="textWriter">The text writer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public static Task ReadAsync(
    this Action<DataReceivedEventHandler> addHandler,
    Action<DataReceivedEventHandler> removeHandler,
    TextWriter textWriter,
    CancellationToken cancellationToken = default(CancellationToken))
{
    var taskCompletionSource = new TaskCompletionSource<object>();

    DataReceivedEventHandler handler = null;
    handler = new DataReceivedEventHandler(
        (sender, e) =>
        {
            if (e.Data == null)
            {
                removeHandler(handler);
                taskCompletionSource.TrySetResult(null);
            }
            else
            {
                textWriter.WriteLine(e.Data);
            }
        });

    addHandler(handler);

    if (cancellationToken != default(CancellationToken))
    {
        cancellationToken.Register(
            () =>
            {
                removeHandler(handler);
                taskCompletionSource.TrySetCanceled();
            });
    }

    return taskCompletionSource.Task;
}

Rob atendeu e me salvou mais algumas horas de ensaios. Leia o buffer de saída / erro antes de espera:

// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

Temos este problema também (ou uma variante).

Tente o seguinte:

1) Adicionar um tempo limite para p.WaitForExit (nnnn); onde nnnn é em milissegundos.

2) Coloque a chamada ReadToEnd antes da chamada WaitForExit. Este é MS que vimos recomendar.

Crédito para EM0 para https://stackoverflow.com/a/17600012/4151626

As outras soluções (incluindo EM0 de) ainda impasse para a minha candidatura, devido a limites de tempo internas e o uso de ambos StandardOutput e StandardError pelo aplicativo gerado. Aqui está o que funcionou para mim:

Process p = new Process()
{
  StartInfo = new ProcessStartInfo()
  {
    FileName = exe,
    Arguments = args,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true
  }
};
p.Start();

string cv_error = null;
Thread et = new Thread(() => { cv_error = p.StandardError.ReadToEnd(); });
et.Start();

string cv_out = null;
Thread ot = new Thread(() => { cv_out = p.StandardOutput.ReadToEnd(); });
ot.Start();

p.WaitForExit();
ot.Join();
et.Join();

Edit: inicialização adicional de StartInfo a amostra de código

Eu resolvi desta forma:

            Process proc = new Process();
            proc.StartInfo.FileName = batchFile;
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;      
            proc.Start();
            StreamWriter streamWriter = proc.StandardInput;
            StreamReader outputReader = proc.StandardOutput;
            StreamReader errorReader = proc.StandardError;
            while (!outputReader.EndOfStream)
            {
                string text = outputReader.ReadLine();                    
                streamWriter.WriteLine(text);
            }

            while (!errorReader.EndOfStream)
            {                   
                string text = errorReader.ReadLine();
                streamWriter.WriteLine(text);
            }

            streamWriter.Close();
            proc.WaitForExit();

Eu redirecionado ambos entrada, saída e erro e leitura tratado de fluxos de saída e de erro. Esta solução funciona para SDK 7 8,1, tanto para Windows 7 e Windows 8

Este post talvez ultrapassada, mas eu descobri a causa principal pela qual ele normalmente jeito é devido ao estouro de pilha para o redirectStandardoutput ou se você tiver redirectStandarderror.

Como os dados de saída ou os dados de erro é grande, ele irá causar um tempo de suspensão, uma vez que ainda está processando por duração indeterminada.

de modo a resolver este problema:

p.StartInfo.RedirectStandardoutput = False
p.StartInfo.RedirectStandarderror = False

Eu tentei fazer uma classe que iria resolver o problema utilizando leitura fluxo assíncrono, tomando em conta Mark Byers, Rob, respostas stevejay. Se o fizer, eu percebi que há um bug relacionado ao processo assíncrono fluxo de saída de leitura.

Eu informou que bug no Microsoft: https://connect.microsoft.com/ VisualStudio / feedback / details / 3119134

Sumário:

Você não pode fazer isso:

process.BeginOutputReadLine (); Process.Start ();

Você receberá System.InvalidOperationException: StandardOut tem não foi redirecionado ou o processo ainda não começou.

=============================================== ================================================== ===========================

Em seguida, você tem que começar a leitura de saída assíncrona após o processo é começou:

Process.Start (); process.BeginOutputReadLine ();

Se o fizer, faça uma condição de corrida, porque o fluxo de saída pode receber dados antes de configurá-lo para assíncrona:

process.Start(); 
// Here the operating system could give the cpu to another thread.  
// For example, the newly created thread (Process) and it could start writing to the output
// immediately before next line would execute. 
// That create a race condition.
process.BeginOutputReadLine();

=============================================== ================================================== ===========================

Em seguida, algumas pessoas poderiam dizer que você só tem que ler o fluxo antes de configurá-lo para assíncrona. Mas ocorre o mesmo problema. Lá será uma condição de corrida entre a leitura síncrona e definir o transmitir em modo assíncrono.

=============================================== ================================================== ===========================

Não há nenhuma maneira de conseguir leitura assíncrona segura de um fluxo de saída de um processo no "Processo" real caminho e "ProcessStartInfo" tem foi concebido.

Você é provavelmente melhor usar leitura assíncrona como sugerido por outros usuários para o seu caso. Mas você deve estar ciente de que você pode perder algumas informações devido à condição de corrida.

Nenhuma das respostas acima está fazendo o trabalho.

Rob trava de solução e solução 'Mark Byers' obter a exceção disposta. (Eu tentei as "soluções" de outras respostas).

Então eu decidi sugerir outra solução:

public void GetProcessOutputWithTimeout(Process process, int timeoutSec, CancellationToken token, out string output, out int exitCode)
{
    string outputLocal = "";  int localExitCode = -1;
    var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        outputLocal = process.StandardOutput.ReadToEnd();
        process.WaitForExit();
        localExitCode = process.ExitCode;
    }, token);

    if (task.Wait(timeoutSec, token))
    {
        output = outputLocal;
        exitCode = localExitCode;
    }
    else
    {
        exitCode = -1;
        output = "";
    }
}

using (var process = new Process())
{
    process.StartInfo = ...;
    process.Start();
    string outputUnicode; int exitCode;
    GetProcessOutputWithTimeout(process, PROCESS_TIMEOUT, out outputUnicode, out exitCode);
}

Este código depurado e funciona perfeitamente.

Introdução

resposta atualmente aceita não funciona (lança exceção) e há muitas soluções, mas nenhum código completo. Esta é, obviamente, muita perda de tempo das pessoas, porque esta é uma questão popular.

A combinação de resposta Mark Byers' ea resposta de Karol Tyl eu escrevi código completo com base em como eu quero usar o método Process.Start.

Uso

Eu usei-o para criar o progresso de diálogo comandos torno git. Isto é como eu usei-o:

    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;
    }

Em teoria, você pode também combinar stdout e stderr, mas eu não testei isso.

Código

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);
                }
            }
        }
    }
}

Eu sei que esta é a ceia de idade, mas, depois de ler esta página inteira nenhuma das soluções foi de trabalho para mim, embora eu não tentar Muhammad Rehan como o código foi um pouco difícil de seguir, embora eu acho que ele estava em o caminho certo. Quando eu digo que não fez trabalho que não é inteiramente verdade, às vezes ele iria trabalhar bem, eu acho que é algo a ver com o comprimento da saída antes de uma marca EOF.

De qualquer forma, a solução que funcionou para mim foi a utilização de diferentes threads para ler a StandardOutput e StandardError e escrever as mensagens.

        StreamWriter sw = null;
        var queue = new ConcurrentQueue<string>();

        var flushTask = new System.Timers.Timer(50);
        flushTask.Elapsed += (s, e) =>
        {
            while (!queue.IsEmpty)
            {
                string line = null;
                if (queue.TryDequeue(out line))
                    sw.WriteLine(line);
            }
            sw.FlushAsync();
        };
        flushTask.Start();

        using (var process = new Process())
        {
            try
            {
                process.StartInfo.FileName = @"...";
                process.StartInfo.Arguments = $"...";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;

                process.Start();

                var outputRead = Task.Run(() =>
                {
                    while (!process.StandardOutput.EndOfStream)
                    {
                        queue.Enqueue(process.StandardOutput.ReadLine());
                    }
                });

                var errorRead = Task.Run(() =>
                {
                    while (!process.StandardError.EndOfStream)
                    {
                        queue.Enqueue(process.StandardError.ReadLine());
                    }
                });

                var timeout = new TimeSpan(hours: 0, minutes: 10, seconds: 0);

                if (Task.WaitAll(new[] { outputRead, errorRead }, timeout) &&
                    process.WaitForExit((int)timeout.TotalMilliseconds))
                {
                    if (process.ExitCode != 0)
                    {
                        throw new Exception($"Failed run... blah blah");
                    }
                }
                else
                {
                    throw new Exception($"process timed out after waiting {timeout}");
                }
            }
            catch (Exception e)
            {
                throw new Exception($"Failed to succesfully run the process.....", e);
            }
        }
    }

Espero que isso ajude alguém, que pensei que isso poderia ser tão difícil!

Depois de ler todas as mensagens aqui, me acomodei na solução consolidada de Marko Avlijaš. No entanto , não resolveu todos os meus problemas.

Em nosso meio temos um serviço do Windows que está programado para executar centenas de diferentes .bat .cmd .exe, ... etc arquivos que se acumularam ao longo dos anos e foram escritos por muitas pessoas diferentes e em diferentes estilos. Nós não temos nenhum controle sobre a escrita dos programas e roteiros, nós somos apenas responsáveis ??pela programação, execução e elaboração de relatórios sobre o sucesso / fracasso.

Então, eu tentei praticamente todas as sugestões aqui com diferentes níveis de sucesso. A resposta de Marko foi quase perfeita, mas quando executado como um serviço, ele didnt sempre stdout captura. Nunca cheguei ao fundo do por que não.

A única solução que encontramos que funciona em todos os nossos casos é este: http://csharptest.net/319/using-the-processrunner-class/index.html

Solução acabei usando para evitar toda a complexidade:

var outputFile = Path.GetTempFileName();
info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args) + " > " + outputFile + " 2>&1");
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(File.ReadAllText(outputFile)); //need the StandardOutput contents

Então eu criar um arquivo temporário, redirecionar tanto a saída e erro a ele usando > outputfile > 2>&1 e depois é só ler o arquivo depois que o processo tenha terminado.

As outras soluções são muito bem para cenários onde você quer fazer outras coisas com a saída, mas para coisas simples que evita um monte de complexidade.

I coisa que isso é simples e melhor abordagem (não precisamos AutoResetEvent):

public static string GGSCIShell(string Path, string Command)
{
    using (Process process = new Process())
    {
        process.StartInfo.WorkingDirectory = Path;
        process.StartInfo.FileName = Path + @"\ggsci.exe";
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;
        process.StartInfo.UseShellExecute = false;

        StringBuilder output = new StringBuilder();
        process.OutputDataReceived += (sender, e) =>
        {
            if (e.Data != null)
            {
                output.AppendLine(e.Data);
            }
        };

        process.Start();
        process.StandardInput.WriteLine(Command);
        process.BeginOutputReadLine();


        int timeoutParts = 10;
        int timeoutPart = (int)TIMEOUT / timeoutParts;
        do
        {
            Thread.Sleep(500);//sometimes halv scond is enough to empty output buff (therefore "exit" will be accepted without "timeoutPart" waiting)
            process.StandardInput.WriteLine("exit");
            timeoutParts--;
        }
        while (!process.WaitForExit(timeoutPart) && timeoutParts > 0);

        if (timeoutParts <= 0)
        {
            output.AppendLine("------ GGSCIShell TIMEOUT: " + TIMEOUT + "ms ------");
        }

        string result = output.ToString();
        return result;
    }
}

Eu acho que com assíncrono, é possível ter uma solução mais elegante e não ter impasses mesmo quando se usa tanto standardOutput e standardError:

using (Process process = new Process())
{
    process.StartInfo.FileName = filename;
    process.StartInfo.Arguments = arguments;
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;

    process.Start();

    var tStandardOutput = process.StandardOutput.ReadToEndAsync();
    var tStandardError = process.StandardError.ReadToEndAsync();

    if (process.WaitForExit(timeout))
    {
        string output = await tStandardOutput;
        string errors = await tStandardError;

        // Process completed. Check process.ExitCode here.
    }
    else
    {
        // Timed out.
    }
}

Ele é a base sobre Mark Byers resposta. Se você não está em um método assíncrono, você pode usar string output = tStandardOutput.result; vez de await

Vamos chamar o código de exemplo postado aqui o redirecionador e o outro programa do redirecionado. Se fosse comigo, então eu provavelmente escrever um programa redirecionado teste que pode ser usado para duplicar o problema.

Então eu fiz. Para dados de teste eu usei o PDF ECMA-334 Linguagem C # Specificationv; trata-se de 5MB. O seguinte é a parte mais importante do que isso.

StreamReader stream = null;
try { stream = new StreamReader(Path); }
catch (Exception ex)
{
    Console.Error.WriteLine("Input open error: " + ex.Message);
    return;
}
Console.SetIn(stream);
int datasize = 0;
try
{
    string record = Console.ReadLine();
    while (record != null)
    {
        datasize += record.Length + 2;
        record = Console.ReadLine();
        Console.WriteLine(record);
    }
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Error: {ex.Message}");
    return;
}

O valor datasize não corresponde ao tamanho do arquivo real, mas isso não importa. Não está claro se um arquivo PDF sempre usa tanto CR e LF no fim de linhas, mas isso não importa para isso. Você pode usar qualquer outro arquivo de texto grande para teste com.

Usando que os trava de código de exemplo redirecionador quando eu escrever a grande quantidade de dados, mas não quando eu escrevo uma pequena quantidade.

Eu tentei muito para traçar alguma forma a execução desse código e eu não podia. Comentei as linhas do programa redirecionada que a criação desativado de um console para o programa redirecionado para tentar obter uma janela de console separado, mas eu não podia.

Então eu achei Como iniciar um aplicativo console em uma nova janela, a janela do pai, ou nenhuma janela . Então, aparentemente, não podemos (facilmente) tem um console separado quando um programa de console inicia outro programa de console sem ShellExecute e desde ShellExecute não suporta redirecionamento devemos compartilhar um console, mesmo que especifique nenhuma janela para o outro processo.

Eu presumo que se o programa redirecionada enche um buffer em algum lugar, então ele deve esperar que os dados sejam lidos e se nesse momento nenhum dado é lido pelo redirecionador então é um impasse.

A solução é não usar ReadToEnd e ler os dados enquanto os dados está sendo escrito, mas não é necessário usar assíncrono lê. A solução pode ser bastante simples. Os seguintes trabalhos para me com o 5 MB PDF.

ProcessStartInfo info = new ProcessStartInfo(TheProgram);
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
string record = p.StandardOutput.ReadLine();
while (record != null)
{
    Console.WriteLine(record);
    record = p.StandardOutput.ReadLine();
}
p.WaitForExit();

Outra possibilidade é usar um programa GUI para fazer o redirecionamento. O código anterior funciona em um aplicativo WPF, exceto com modificações óbvias.

Eu estava tendo o mesmo problema, mas a razão era diferente. Seria, no entanto acontecer no Windows 8, mas não no Windows 7. A seguinte linha parece ter causado o problema.

pProcess.StartInfo.UseShellExecute = False

A solução foi a de não desativar UseShellExecute. I já recebeu uma janela pop-up Shell, que é indesejável, mas muito melhor do que o programa espera de nada em particular a acontecer. Então eu adicionei o seguinte solução alternativa para isso:

pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden

Agora, a única coisa que me incomoda é porque isto está acontecendo sob o Windows 8 em primeiro lugar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top