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 estou iniciando tem cerca de 7 MB. Executá-lo no console do Windows funciona bem. Infelizmente, programaticamente, isso trava indefinidamente em WaitForExit. Observe também que este código NÃO trava para saídas menores (como 3 KB).
É possível que o StandardOutput interno no ProcessStartInfo não consiga armazenar em buffer 7 MB? Se sim, o que devo fazer? Se não, o que estou fazendo de errado?
c#
processstartinfo
Epaga
fonte
fonte
Respostas:
O problema é que, se você redirecionar
StandardOutput
e / ouStandardError
o buffer interno pode ficar cheio. Qualquer que seja a ordem usada, pode haver um problema:StandardOutput
o processo, poderá bloquear a tentativa de gravar nele, para que o processo nunca termine.StandardOutput
ReadToEnd, seu processo poderá ser bloqueado se o processo nunca for fechadoStandardOutput
(por exemplo, se ele nunca for encerrado ou se estiver bloqueado para a gravaçãoStandardError
).A solução é usar leituras assíncronas para garantir que o buffer não fique cheio. Para evitar impasses e recolher-se todas as saídas de ambos
StandardOutput
eStandardError
você pode fazer isso:EDIT: consulte as respostas abaixo para saber como evitar uma ObjectDisposedException se o tempo limite ocorrer.
fonte
using
instruções para os manipuladores de eventos precisem estar acima dausing
instrução para o próprio processo?A documentação para
Process.StandardOutput
diz para ler antes de esperar, caso contrário, você pode obter um impasse, trecho copiado abaixo:fonte
RedirectStandardOutput = true;
e não usa,p.StandardOutput.ReadToEnd();
obtém um impasse / travamento.A resposta de Mark Byers é excelente, mas gostaria de acrescentar o seguinte:
Os delegados
OutputDataReceived
eErrorDataReceived
precisam ser removidos antesoutputWaitHandle
eerrorWaitHandle
descartados. Se o processo continuar produzindo dados após o tempo limite exceder e terminar, as variáveisoutputWaitHandle
eerrorWaitHandle
serão acessadas após serem descartadas.(Para sua informação, tive que acrescentar esta advertência como resposta, pois não podia comentar sua postagem.)
fonte
Esta é uma solução mais moderna e esperada, baseada em Task Parallel Library (TPL) para .NET 4.5 e superior.
Exemplo de uso
Implementação
fonte
O problema com ObjectDisposedException sem tratamento ocorre quando o tempo limite do processo é excedido. Nesse caso, as outras partes da condição:
não são executados. Resolvi esse problema da seguinte maneira:
fonte
output
eerror
paraoutputBuilder
? Alguém pode fornecer uma resposta completa que funcione?Rob atendeu e me salvou mais algumas horas de testes. Leia o buffer de saída / erro antes de esperar:
fonte
WaitForExit()
?ReadToEnd
ou métodos semelhantes (comoStandardOutput.BaseStream.CopyTo
) retornarão após a leitura de TODOS os dados. nada virá depois disso.Também temos esse problema (ou uma variante).
Tente o seguinte:
1) Adicione um tempo limite a p.WaitForExit (nnnn); onde nnnn está em milissegundos.
2) Coloque a chamada ReadToEnd antes da chamada WaitForExit. Isso é o que temos visto MS recomendar.
fonte
Crédito para EM0 por https://stackoverflow.com/a/17600012/4151626
As outras soluções (incluindo EM0) ainda estão bloqueadas para o meu aplicativo, devido a tempos limites internos e ao uso de StandardOutput e StandardError pelo aplicativo gerado. Aqui está o que funcionou para mim:
Editar: adicionada inicialização do StartInfo ao exemplo de código
fonte
Eu resolvi assim:
Redirecionei a entrada, a saída e o erro e lidei com a leitura dos fluxos de saída e erro. Esta solução funciona para o SDK 7- 8.1, tanto no Windows 7 quanto no Windows 8
fonte
Tentei fazer uma classe que resolvesse seu problema usando a leitura assíncrona do fluxo, levando em consideração as respostas de Mark Byers, Rob e stevejay. Ao fazer isso, percebi que havia um erro relacionado à leitura assíncrona do fluxo de saída do processo.
Eu relatei esse bug na Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/3119134
Resumo:
Provavelmente é melhor usar a leitura assíncrona, como sugerido por outros usuários para o seu caso. Mas você deve estar ciente de que poderá perder algumas informações devido às condições da corrida.
fonte
Eu acho que essa é uma abordagem simples e melhor (não precisamos
AutoResetEvent
):fonte
.FileName = Path + @"\ggsci.exe" + @" < obeycommand.txt"
para simplificar seu código também? Ou talvez algo equivalente a"echo command | " + Path + @"\ggsci.exe"
se você realmente não deseja usar um arquivo obeycommand.txt separado.Nenhuma das respostas acima está fazendo o trabalho.
A solução Rob trava e a solução 'Mark Byers' recebe a exceção descartada (tentei as "soluções" das outras respostas).
Então decidi sugerir outra solução:
Este código depurou e funciona perfeitamente.
fonte
GetProcessOutputWithTimeout
método.Introdução
A resposta atualmente aceita não funciona (lança exceção) e há muitas soluções alternativas, mas nenhum código completo. Obviamente, isso está desperdiçando muito tempo das pessoas, porque esta é uma pergunta popular.
Combinando a resposta de Mark Byers e a resposta de Karol Tyl, escrevi o código completo com base em como desejo usar o método Process.Start.
Uso
Eu o usei para criar uma caixa de diálogo de progresso em torno dos comandos git. É assim que eu o usei:
Em teoria, você também pode combinar stdout e stderr, mas eu não testei isso.
Código
fonte
Eu sei que esse é o jantar antigo, mas, depois de ler esta página inteira, nenhuma das soluções funcionou para mim, embora eu não tenha experimentado Muhammad Rehan, pois o código era um pouco difícil de seguir, embora eu ache que ele estava no caminho certo . Quando digo que não funcionou, isso não é inteiramente verdade, às vezes funcionaria bem, acho que tem 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 usar threads diferentes para ler o StandardOutput e o StandardError e escrever as mensagens.
Espero que isso ajude alguém que pensou que isso poderia ser tão difícil!
fonte
sw.FlushAsync(): Object is not set to an instance of an object. sw is null.
Como / onde devesw
ser definido?Depois de ler todas as postagens aqui, decidi pela solução consolidada de Marko Avlijaš. No entanto , não resolveu todos os meus problemas.
Em nosso ambiente, temos um serviço do Windows que está programado para executar centenas de arquivos .bat .cmd .exe, ... etc. diferentes que se acumularam ao longo dos anos e foram escritos por muitas pessoas diferentes e em estilos diferentes. Não temos controle sobre a gravação dos programas e scripts, somos apenas responsáveis pelo agendamento, execução e relatório de sucesso / falha.
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 executada como um serviço, ela nem sempre capturava o padrão. Eu nunca cheguei ao fundo do porque não.
A única solução que encontramos que funciona em TODOS os nossos casos é a seguinte: http://csharptest.net/319/using-the-processrunner-class/index.html
fonte
Solução alternativa que acabei usando para evitar toda a complexidade:
Então, eu crio um arquivo temporário, redireciono a saída e o erro para ele usando
> outputfile > 2>&1
e depois leio o arquivo após o término do processo.As outras soluções são adequadas para cenários em que você deseja fazer outras coisas com a saída, mas para coisas simples, isso evita muita complexidade.
fonte
Eu li muitas das respostas e fiz as minhas. Não tenho certeza se este será corrigido em qualquer caso, mas no meu ambiente. Eu apenas não estou usando WaitForExit e uso WaitHandle.WaitAll nos sinais de saída e erro final. Ficarei feliz se alguém perceber possíveis problemas com isso. Ou se isso vai ajudar alguém. Para mim, é melhor porque não usa tempos limite.
fonte
Eu acho que com o assíncrono, é possível ter uma solução mais elegante e não ter impasses, mesmo ao usar o standardOutput e o standardError:
É baseado na resposta de Mark Byers. Se você não estiver em um método assíncrono, poderá usar em
string output = tStandardOutput.result;
vez deawait
fonte
Nenhuma dessas respostas me ajudou, mas essa solução funcionou bem no manuseio de travamentos
https://stackoverflow.com/a/60355879/10522960
fonte
Esta postagem pode estar desatualizada, mas descobri a principal causa pela qual geralmente travar devido ao estouro de pilha do redirectStandardoutput ou se você tiver redirectStandarderror.
Como os dados de saída ou os dados de erro são grandes, isso causa um tempo de espera, pois ainda está sendo processado por tempo indeterminado.
para resolver esse problema:
fonte
Vamos chamar o código de exemplo postado aqui como redirecionador e o outro programa como redirecionado. Se fosse eu, provavelmente escreveria um programa de teste redirecionado que pode ser usado para duplicar o problema.
Então eu fiz. Para dados de teste, usei o PDF de Especificação de Linguagem CMA ECMA-334; é cerca de 5MB. A seguir, é a parte importante disso.
O valor do tamanho dos dados não corresponde ao tamanho real do arquivo, mas isso não importa. Não está claro se um arquivo PDF sempre usa CR e LF no final das linhas, mas isso não importa. Você pode usar qualquer outro arquivo de texto grande para testar.
Usando isso, o código de redirecionador de amostra trava quando eu escrevo a grande quantidade de dados, mas não quando eu escrevo uma pequena quantidade.
Tentei muito rastrear a execução desse código e não consegui. Comentei as linhas do programa redirecionado que desabilitaram a criação de um console para o programa redirecionado para tentar obter uma janela separada do console, mas não consegui.
Então eu encontrei Como iniciar um aplicativo de console em uma nova janela, na janela dos pais ou em nenhuma janela . Então, aparentemente, não podemos (facilmente) ter um console separado quando um programa de console inicia outro programa de console sem o ShellExecute e, como o ShellExecute não suporta redirecionamento, devemos compartilhar um console, mesmo se não especificarmos nenhuma janela para o outro processo.
Presumo que, se o programa redirecionado preenche um buffer em algum lugar, ele deve aguardar a leitura dos dados e, se nesse ponto, nenhum dado for lido pelo redirecionador, será um impasse.
A solução é não usar ReadToEnd e ler os dados enquanto os dados estão sendo gravados, mas não é necessário usar leituras assíncronas. A solução pode ser bastante simples. O seguinte funciona para mim com o PDF de 5 MB.
Outra possibilidade é usar um programa GUI para fazer o redirecionamento. O código anterior funciona em um aplicativo WPF, exceto com modificações óbvias.
fonte
Eu estava tendo o mesmo problema, mas o motivo era diferente. No entanto, isso aconteceria no Windows 8, mas não no Windows 7. A seguinte linha parece ter causado o problema.
A solução foi NÃO desativar o UseShellExecute. Agora recebi uma janela pop-up do Shell, que é indesejável, mas muito melhor do que o programa esperando que nada de especial aconteça. Então, adicionei a seguinte solução alternativa para isso:
Agora, a única coisa que me incomoda é por que isso está acontecendo no Windows 8 em primeiro lugar.
fonte
UseShellExecute
ser definido como false se desejar redirecionar a saída.