Saídadatecebida após waitforexit


Como usar System. Diagnostics. Process corretamente.


Eu vi muitas perguntas no stackoverflow e em outros lugares sobre a execução de um processo e a captura da saída. Usando o System. Diagnostics. Process corretamente não é fácil e na maioria das vezes é feito errado.


Alguns erros comuns com System. Diagnostics. Process:


Não capturar os dois fluxos de saída (erro e saída) Não redirecionar a entrada pode fazer com que os aplicativos travem Não fechar entrada redirecionada pode fazer com que os aplicativos travem Não chamar BeginOutputReadLine / BeginErrorReadLine ao usar eventos Usar OutputDataReceived / ErrorDataReceived sem aguardar null Não verificar null em OutputDataReceived / ErrorDataReceived handlers Esquecendo de setar EnableRaisingEvents = true; ao usar o evento Exited Forgetting ErrorDialog, CreateNoWindow ou UseShellExecute settings Manipulação incorreta dos leitores de fluxo StandardOutput ou StandardError.


Então, com isto dito, aqui estão algumas diretrizes básicas:


Use os eventos OutputDataReceived / ErrorDataRecieved NOT o StandardOutput ou StandardError. Isso poupará muita dor de cabeça e gerenciamento desnecessário de encadeamentos. Sempre capture todas as saídas E entradas, se você não planeja fornecer entrada, feche o fluxo imediatamente. Seu processo não é feito até que ele tenha saído E você leu todos os dados. OutputDataReceived CAN E será disparado após uma chamada para WaitForExit () retornar. Você precisará de identificadores de espera para cada fluxo de saída e definirá o identificador de espera assim que receber dados (nulos).


Herança Múltipla.


Derivado de muitas coisas.


Processo Async OutputDataReceived / ErrorDataReceived tem uma falha ao lidar com prompts.


Classe System. Diagnostics. Process - Parte 2.


Na Parte 1 eu discuti o problema em usar o redirecionamento stdout / stderr e lê-los de forma síncrona, o buffer de saída pode ficar cheio e bloquear o processo filho na próxima “gravação”. Em seguida, um deadlock ocorre porque o processo pai está aguardando o filho para sair antes de ler dados do buffer, o filho está bloqueando a gravação porque o buffer está cheio, portanto, o deadlock. A resposta é simples & # 8211; É melhor usar o método assíncrono de leitura de dados de stdout / stderr. Esta questão é previsível e foi minha má.


Não sou eu, é você.


Eu me deparo com outro problema, que parecia o mesmo impasse, em que uma má suposição na implementação da classe Process da Microsoft causou um tipo diferente de impasse entre os processos pai e filho. Eu o encontrei quando o processo do meu filho me levou a afirmar a atividade solicitada, veja na Figura 1 um exemplo.


O prompt terminou sem uma nova linha, deixando o cursor no final do prompt (o que faz sentido, por que o cursor estaria configurado sob o prompt e não ao lado dele na tela). Veja a Figura 2, por exemplo, código deste prompt.


Meu processo pai foi codificado para inspecionar os dados stdout recebidos de forma assíncrona, procurando o prompt. Se / quando o aviso for recebido, o processo pai emitirá uma resposta ao status do processo filho. Veja a figura 3.


No entanto, quando executo meu processo pai e o processo filho envia o prompt, o texto nunca veio no evento OutputDataReceived… e meu processo pai permaneceu na linha p. WaitForExit () para sempre… O processo Child estava aguardando uma resposta que nunca veio do pai, e nós estávamos mais uma vez em um impasse, desta vez eu não era eu .... O problema não estava no meu código.


Sem NewLine, sem DataReceivedEvent…


Comecei a depurar e observar os eventos OutputDataReceived e comecei a alterar o código do processo filho de várias maneiras. A pequena alteração abaixo fez com que o evento OutputDataReceived fosse disparado com o texto do prompt, consulte a Figura 4.


De alguns outros testes, parece que o evento OutputDataReceived é acionado somente quando um NewLine está na saída do processo filho. Um prompt sem uma NewLine não acionaria o evento OutputDataReceived e, portanto, o processo pai não obtém o prompt e não pode responder. Impasse!


Implementando minha própria leitura assíncrona de stdout / stderr.


Eu preciso ler prompts de um processo filho que não é o meu código, mas um aplicativo de console de terceiros, então, em última análise, eu precisava ser capaz de receber prompts sem NewLines. Resolver o problema exigiu que eu implementasse minha própria classe Async IO para ler o processo stdout / stderror.


Eu criei uma classe chamada StdStreamReader que lê um fluxo e dispara eventos basicamente semelhantes ao evento Process OutputDataReceived, no entanto, ele não desativa um NewLine para disparar um evento de dados recebidos, mas, em vez disso, dispara assim que os dados são recebidos. Na Figura 5, destaquei as diferenças de código no código do processo pai usando a nova classe de leitura.


O código para StdStreamReader é razoavelmente mundano, mas o resultado foi o esperado, veja a Figura 6.


Observe que "Sim" não foi exibido logo após o prompt - isso é porque foi enviado diretamente para o processo filho stdin e não exibido.


Clique para Código Fonte Completo, que é fornecido “como está”, sem garantia de nenhum tipo.


Compartilhar isso:


Relacionado


Pós-navegação.


Deixe uma resposta Cancelar resposta.


Bom trabalho, esta é a solução exata que eu estava procurando. Obrigado por incluir seu código-fonte!


Graças a um milhão, cara! Aparentemente, esta questão ainda é relevante após 4 anos. Me ajudou muito.


Exemplo de uso.


Eu resolvi assim:


Eu redirecionei tanto a entrada, saída e erro e lidou com a leitura de fluxos de saída e erro. Essa solução funciona para o SDK 7- 8,1, tanto para o Windows 7 quanto para o Windows 8.


Eu tentei fazer uma classe que resolveria seu problema usando leitura de fluxo assíncrona, levando em consideração Mark Byers, Rob, stevejay respostas. Ao fazer isso, percebi que há um erro relacionado à leitura do fluxo de saída do processo assíncrono.


Você não pode fazer isso:


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


Então você precisa iniciar a leitura assíncrona após o processo ser iniciado:


Fazendo isso, faça uma condição de corrida porque o fluxo de saída pode receber dados antes de defini-los como assíncronos:


Então, algumas pessoas podem dizer que você só precisa ler o fluxo antes de defini-lo como assíncrono. Mas o mesmo problema ocorre. Haverá uma condição de corrida entre a leitura síncrona e o fluxo em modo assíncrono.


Não há como obter uma leitura assíncrona segura de um fluxo de saída de um processo na maneira real como "Process" e "ProcessStartInfo" foram projetados.


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


Nenhuma das respostas acima está fazendo o trabalho.


A solução Rob trava e a solução 'Mark Byers' obtém a exceção descartada (tentei as "soluções" das outras respostas).


Então decidi sugerir outra solução:


Este código depurado e funciona perfeitamente.


Introdução.


A resposta aceita atualmente não funciona (gera exceção) e há muitas soluções alternativas, mas nenhum código completo. Isto é, obviamente, desperdiçando muito tempo das pessoas, porque esta é uma questão popular.


Combinando a resposta de Mark Byers e a resposta de Karol Tyl, escrevi o código completo com base em como eu quero usar o método Process. Start.


Eu usei-o para criar um diálogo de progresso em torno dos comandos do git. É assim que eu usei:


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


As outras soluções (incluindo EM0s) ainda estão em deadlock para meu aplicativo, devido a tempos limite 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.


Este post talvez desatualizado, mas eu descobri a causa principal porque normalmente travar é devido ao estouro de pilha para a saída redirectStandard ou se você tiver redirectStandarderror.


Como os dados de saída ou os dados de erro são grandes, isso causará um tempo de interrupção, pois ainda está sendo processado por duração indefinida.


então, para resolver esse problema:


Eu acho que essa é uma abordagem simples e melhor (não precisamos do AutoResetEvent):


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 linha a seguir parece ter causado o problema.


A solução foi NÃO desabilitar o UseShellExecute. Eu agora recebi uma janela popup do Shell, que é indesejada, mas muito melhor do que o programa esperando que nada de especial aconteça. Então eu adicionei a seguinte solução para isso:


Agora, a única coisa que me incomoda é por que isso está acontecendo no Windows 8 em primeiro lugar.


Eu sei que esta é a ceia de idade, mas, depois de ler esta página inteira, nenhuma das soluções estava funcionando para mim, embora eu não tenha tentado Muhammad Rehan como o código foi um pouco difícil de seguir, embora eu acho que ele estava no caminho certo . Quando eu digo que não funcionou isso não é inteiramente verdade, às vezes funcionaria 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 usar diferentes threads para ler o StandardOutput e StandardError e escrever as mensagens.


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


Depois de ler todos os posts aqui, resolvi a solução consolidada de Marko Avlijaš. No entanto, isso 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 diferentes. 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 controle sobre a escrita dos programas & amp; scripts, somos responsáveis ​​apenas pelo agendamento, execução e geração de relatórios sobre 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, nem sempre captava stdout. Eu nunca cheguei ao fundo do por que não.


Obtener la salida de un command de consola.


Para solucionar esse problema, realmente precisamos do código-fonte para reproduzir o problema, para que possamos investigar o problema internamente. Não é necessário que você envie a fonte completa do seu projeto. Nós só precisamos de uma amostra mais simples para reproduzir o problema. Você pode remover qualquer informação confidencial ou lógica de negócios dele.


A leitura do StandardOutput é realmente complicada. O problema é que você também precisa ler o fluxo de erros ao mesmo tempo. Se você não ler constantemente de ambos, um dos buffers poderá ficar cheio e o processo parecerá travar enquanto espera que você o leia. É por isso que você não pode chamar ReadToEnd nos fluxos.


Eu realmente resolvi isso uma vez para o caso geral e eu me chuto toda vez que penso sobre isso, porque eu não pensei em manter uma cópia desse código para mim. Acabei tendo dois threads de leitor processando os fluxos de saída e erro que passariam os resultados de volta ao thread principal.


Se você ainda precisar de ajuda, posso tentar resolver isso amanhã.


Eu escrevi um exemplo de código para mostrar como você pode ler a saída de outro processo de linha de comando:


static void Main (string [] args)


String exeFileName = & quot; output. exe & quot; ;


ProcessStartInfo info = new ProcessStartInfo (exeFileName);


// Redirecionar a saída padrão do processo.


info. RedirectStandardOutput = true;


info. RedirectStandardError = true;


// Defina UseShellExecute como false para redirecionamento.


info. UseShellExecute = false;


Process proc = new Process ();


proc. StartInfo = info;


// Defina nosso manipulador de eventos para ler de forma assíncrona a saída de classificação.


proc. OutputDataReceived + = new DataReceivedEventHandler (proc_OutputDataReceived);


proc. ErrorDataReceived + = new DataReceivedEventHandler (proc_ErrorDataReceived);


// Inicia a leitura assíncrona do fluxo de saída de classificação. Note esta linha!


static void proc_ErrorDataReceived (remetente do objeto, DataReceivedEventArgs e)


Console. WriteLine (& quot; Erro: & quot; e e. Dados);


static void proc_OutputDataReceived (remetente do objeto, DataReceivedEventArgs e)


Console. WriteLine (& quot; Dados de saída: & quot ;, e. Data);


Espero que isto ajude!


Bem, isso é certamente mais fácil do que eu costumava fazer.


Eu realmente resolvi isso uma vez para o caso geral e eu me chuto toda vez que penso sobre isso, porque eu não pensei em manter uma cópia desse código para mim. Acabei tendo dois threads de leitor processando os fluxos de saída e erro que passariam os resultados de volta ao thread principal.


O problema é que, de acordo com a documentação do MSDN (e com os meus próprios testes, que suportaram isso), "Any instance members [da classe Process] não tem garantia de segurança para thread." Portanto, sua solução multi-threading, em teoria, não deveria funcionar. Certamente não é recomendado pela MS. Obrigado pela sugestão embora.


Eu escrevi um exemplo de código para mostrar como você pode ler a saída de outro processo de linha de comando:


Obrigado por isso. Isso realmente parece funcionar (yay!). mas apenas de forma síncrona. Você pode usar esse método para obter com segurança todas as saídas stdout e stderr de um processo, mas o código deve aguardar até que o processo tenha saído antes de retornar. Tudo bem se você quiser lidar com apenas um processo de cada vez, então este é um bom começo. Infelizmente, minha maior ambição era ser capaz de lidar com vários de uma só vez, o que significaria criar um novo thread que chamava esse código para cada processo. O problema é que, como mencionado acima, os membros da instância da classe Process não são thread-safe, então isso não pode ser feito.


Então, obrigado pela ajuda de todos, e agora sabemos como lidar com esse problema de maneira síncrona. No entanto, se houver uma maneira de fazê-lo de forma assíncrona (isto é, disparar alguns threads que lidam com alguns processos diferentes e, em seguida, retornar imediatamente para manter a interface do usuário responsiva), eu gostaria de saber como fazer isso. . Enquanto isso, porém, vou ter que viver fazendo isso de maneira síncrona.


Na verdade, as duas funções do manipulador de eventos foram chamadas pelos encadeamentos em segundo plano do CLR de forma assíncrona. E, se você quiser ouvir vários processos, por que não usar vários objetos da classe Process? Você pode usar uma função de manipulador de eventos para manipular eventos de fontes de eventos mulitiple.


Feng, esta é uma boa solução se você só precisa da saída & quot; depois & quot; você chamou proc. WaitForExit ().


Eu preciso disso colocar "durante" meu processo de ftp.


Veja abaixo, depois que eu enviar o comando OPEN, preciso verificar para ver o que o.


código de retorno do servidor foi.


Se foi um 220, então eu envio meu nome de usuário e senha e, em seguida, preciso ver se.


Eu tenho um código de 200 de volta.


E se eu estou encontrando é que o DataReceivedEventHandler (FTPProcess_OutputDataReceived)


O evento não dispara até depois do WaitForExit, que é tarde demais para as minhas necessidades.


Alguma idéia sobre como obter dados em tempo real do StandardOutput?


// código do processo de configuração.


configuração bool privada ()


FTPProcess. OutputDataReceived + = new DataReceivedEventHandler (FTPProcess_OutputDataReceived);


FTPProcess. ErrorDataReceived + = new DataReceivedEventHandler (FTPProcess_ErrorDataReceived);


private bool LoginToServer ()


// Enviando comandos para o processo FTP.


FTPStreamWriter. WriteLine (& quot; ABRIR & quot; + este ._FtpServerAddress);


bool success = false;


// se tivermos nos conectado com sucesso ao servidor ftp.


FTPStreamWriter. WriteLine (& quot; USER & quot; + this. _UserName + & quot; & quot; + esta ._Password);


// se tivermos um login bem-sucedido.


Vazio privado FTPProcess_OutputDataReceived (objeto sendingProcess, saída DataReceivedEventArgs)


if (! string. IsNullOrEmpty (output. Data))


FTP privado void FTPProcess_ErrorDataReceived (objeto sendingProcess, erro DataReceivedEventArgs)


if (! string. IsNullOrEmpty (error. Data))


O evento Exited é acionado antes do último evento OuputDataReceived. Como resultado, você não sabe quando o último evento OutputDataReceived chegou (suponho que isso também seja verdade para ErrorDataReceived).


comando string = & quot; output. exe & quot; ;


argumentos de string = & quot; o que quer que seja? ;


proc. OutputDataReceived + = new DataReceivedEventHandler (proc_OutputDataReceived);


proc. ErrorDataReceived + = new DataReceivedEventHandler (proc_ErrorDataReceived);


proc. Exited + = new EventHandler (proc_Exited);


Console. WriteLine (& quot; Exited (Event) & quot;);


Console. WriteLine (& quot; Erro: & quot; e. Data);


Console. WriteLine (& quot; Dados de saída: & quot; e. Data);


Portanto, se você capturar que os fluxos estão fechados e capturar o evento de saída, será possível detectar com precisão a saída + IO concluída.


& lt; Sim, sei que este é um assunto antigo & gt;


Verificar se os dados são nulos ou vazios não é o melhor caminho a percorrer. É possível que o programa que você está executando esteja exibindo strings vazias.


Tanto quanto eu posso dizer, não há maneira certa de saber quando os eventos de IO foram concluídos. Mesmo "elggarc" resposta acima não garante isso.


A única maneira que encontrei é dormir o seu fio principal por um segundo antes de voltar. Exemplo tomando a resposta de cima:


Editado por pbyrne quinta-feira, 3 de março de 2011 22:34 fixing example.


A documentação diz sobre BeginOutputReadLine:


Inicia operações de leitura assíncrona no fluxo StandardOutput redirecionado do aplicativo.


No entanto, parece que você está tentando redirecionar stderr e stdout. Vai chamar apenas BeginOutputReadLine () também ler mensagem de stderr?


Este é o melhor, depois de uma grande escavação.


teste estático público void ()


System. Diagnostics. Process winscp = new System. Diagnostics. Process ();


string result = RunCommand (winscp, & quot; Dir & quot;);


result = RunCommand (winscp, & quot; Dir & quot;);


result = RunCommand (winscp, & quot; Dir & quot;);


result = RunCommand (winscp, & quot; Dir & quot;);


result = RunCommand (winscp, & quot; Dir & quot;);


Tópico threadToKill = null;


Func & lt; System. IO. StreamReader, StringBuilder, string & gt; wrappedAction = (reader, sb) = & gt;


int cnt = reader. Peek ();


char [] buffer = novo char [cnt];


int rcnt = reader. Read (buffer, 0, cnt);


StringBuilder sbresult = novo StringBuilder ();


Resultado de IAsyncResult = wrappedAction. BeginInvoke (winscp. StandardOutput, sbresult, null, null);


if (result. AsyncWaitHandle. WaitOne (new TimeSpan (0, 0, timout)))


A Microsoft está realizando uma pesquisa online para entender sua opinião sobre o site da MSDN. Se você optar por participar, a pesquisa on-line será apresentada quando você sair do site do Msdn.


Processo . Evento OutputDataReceived.


A documentação de referência da API tem uma nova casa. Visite o Navegador da API em docs. microsoft para ver a nova experiência.


Ocorre toda vez que um aplicativo grava uma linha em seu fluxo StandardOutput redirecionado.


Assembly: System (no System. dll)


O evento OutputDataReceived indica que o processo associado gravou uma linha, terminando com um caractere de nova linha, para seu fluxo StandardOutput redirecionado.


O evento é ativado durante operações de leitura assíncrona no StandardOutput. Para iniciar operações de leitura assíncrona, você deve redirecionar o fluxo StandardOutput de um processo, adicionar seu manipulador de eventos ao evento OutputDataReceived e chamar BeginOutputReadLine. Posteriormente, o evento OutputDataReceived sinaliza cada vez que o processo grava uma linha no fluxo StandardOutput redirecionado, até que o processo saia ou chame CancelOutputRead.


O aplicativo que está processando a saída assíncrona deve chamar o método WaitForExit para garantir que o buffer de saída tenha sido liberado.


O exemplo a seguir ilustra como executar operações de leitura assíncrona no fluxo StandardOutput redirecionado do comando ipconfig.


O exemplo cria um delegado de evento para o manipulador de eventos OutputHandler e o associa ao evento OutputDataReceived. O manipulador de eventos recebe linhas de texto do fluxo StandardOutput redirecionado, formata o texto e as salva em uma string de saída que é mostrada posteriormente na janela do console do exemplo.


para confiança total para o chamador imediato. Este membro não pode ser usado por código parcialmente confiável.


Processo . Método BeginOutputReadLine ()


A documentação de referência da API tem uma nova casa. Visite o Navegador da API em docs. microsoft para ver a nova experiência.


Inicia operações de leitura assíncrona no fluxo StandardOutput redirecionado do aplicativo.


Assembly: System (no System. dll)


Uma operação de leitura assíncrona já está em andamento no fluxo StandardOutput.


O fluxo StandardOutput foi usado por uma operação de leitura síncrona.


O fluxo StandardOutput pode ser lido de forma síncrona ou assíncrona. Métodos como Read, ReadLine e ReadToEnd executam operações de leitura síncrona no fluxo de saída do processo. Essas operações de leitura síncrona não são concluídas até que o Processo associado grave em seu fluxo StandardOutput ou feche o fluxo.


Por outro lado, BeginOutputReadLine inicia operações de leitura assíncrona no fluxo StandardOutput. Esse método ativa um manipulador de eventos designado para a saída do fluxo e retorna imediatamente ao responsável pela chamada, que pode executar outro trabalho enquanto a saída do fluxo é direcionada para o manipulador de eventos.


Siga estas etapas para executar operações de leitura assíncrona no StandardOutput para um processo:


Adicione seu manipulador de eventos ao evento OutputDataReceived. O manipulador de eventos deve corresponder ao System. Diagnostics. Assinatura de delegado DataReceivedEventHandler.


Chame BeginOutputReadLine para o processo. Esta chamada inicia operações de leitura assíncrona no StandardOutput.


Quando operações de leitura assíncrona são iniciadas, o manipulador de eventos é chamado toda vez que o Processo associado grava uma linha de texto em seu fluxo StandardOutput.


Você pode cancelar uma operação de leitura assíncrona chamando CancelOutputRead. A operação de leitura pode ser cancelada pelo chamador ou pelo manipulador de eventos. Após o cancelamento, você pode chamar BeginOutputReadLine novamente para retomar as operações de leitura assíncrona.


Você não pode mesclar operações de leitura assíncrona e síncrona em um fluxo redirecionado. Depois que o fluxo redirecionado de um processo é aberto no modo assíncrono ou síncrono, todas as operações de leitura adicionais nesse fluxo devem estar no mesmo modo. Por exemplo, não siga BeginOutputReadLine com uma chamada para ReadLine no fluxo StandardOutput ou vice-versa. No entanto, você pode ler dois fluxos diferentes em modos diferentes. Por exemplo, você pode chamar BeginOutputReadLine e, em seguida, chamar ReadLine para o fluxo StandardError.


O exemplo a seguir ilustra como executar operações de leitura assíncrona no fluxo StandardOutput redirecionado do comando sort. O comando sort é um aplicativo de console que lê e classifica a entrada de texto.


O exemplo cria um delegado de evento para o manipulador de eventos SortOutputHandler e o associa ao evento OutputDataReceived. O manipulador de eventos recebe linhas de texto do fluxo StandardOutput redirecionado, formata o texto e grava o texto na tela.


para confiança total para o chamador imediato. Este membro não pode ser usado por código parcialmente confiável.

Comments

Popular Posts