Outputdatareceived after waitforexit
Esperando um processo com tempo limite em.
Na mesma linha da minha última publicação, podemos querer ser mais inteligentes sobre o lançamento de um processo e aguardamos a saída.
Particularmente, se esse processo for parte de um sistema crítico, como seu processo de compilação, você não quer que um processo suspenso seja capaz de fazer com que seu sistema de compilação inteiro pare de morrer nas suas faixas.
Felizmente, o Processo tem uma sobrecarga de WaitForExit que leva um tempo limite inteiro e retorna um booleano - verdadeiro se o processo for encerrado ou falso se o processo não acontecesse.
Usando esta função, podemos escrever um método como este:
Neste trecho, OutputDataReceived é um evento que dispara quando o processo grava dados em saída padrão e sinais BeginOutputReadLine para que configuramos nosso evento e estamos prontos para começar a ouvir a saída do processo.
Usar o formulário de evento neste caso significa que não precisamos ter o texto de saída completo do processo na memória em nenhum momento. À medida que o processo escreve uma linha de texto, teremos apenas essa linha na memória, e isso será coletado por lixo depois de atualizar nosso contador.
Há dois "gotcha" s neste trecho, no entanto.
O primeiro é process. Kill (). Esta é uma condição de corrida clássica - entre o tempo em que nosso processo retorna de process. WaitForExit () e invoca processo. Kill (), o processo de destino pode sair. Nesse caso, o MSDN afirma que, se você chamar. Kill () em um processo que já saiu, você receberá uma InvalidOperationException.
Podemos corrigir isso, manipulando a exceção, assim:
O segundo "gotcha" é muito sutil, e você provavelmente não o achará até que a função não retorne o resultado esperado, ou até se sentar e ler toda a página de documentação para Process. WaitForExit (int). O MDSN afirma que:
Quando a saída padrão foi redirecionada para manipuladores de eventos assíncronos, é possível que o processamento de saída não seja concluído quando esse método retornar. Para garantir que o tratamento de eventos assíncrono tenha sido concluído, chame a sobrecarga WaitForExit () que não leva nenhum parâmetro depois de receber uma verdade dessa sobrecarga.
Nesse caso, é possível que nossa função GetNumOutputChars devolva um valor que contenha menos caracteres do que a saída real do processo, a menos que chamemos WaitForExit () novamente.
Com isso em mente, nossa função de segurança final agora se parece com isto:
Outputdatareceived after waitforexit
Obter através da App Store Leia esta publicação em nosso aplicativo!
Como aguardar os fluxos de saída quando Process. WaitForExit é usado com tempo limite difícil.
Isso tem sido uma espécie de "pesquisa criminal" com resultados muito surpreendentes - pelo menos para mim.
Estamos executando comandos externos usando um objeto Processo. Estamos capturando a saída padrão (e erro padrão - eu removi isso da amostra por motivos de clareza) de forma assíncrona. Isso foi respondido várias vezes aqui. O código parece assim e funciona bem:
Agora, e se eu quiser adicionar um tempo limite difícil ao WaitForExit ()? Fácil: existe uma versão sobrecarregada do WaitForExit (int milissegundos). Então, uma chamada como essa deveria:
Agora, isso torna a captura de saída incompleta em alguns casos.
Isso não é um segredo, pelo menos não para os desenvolvedores da Microsoft. Após várias horas de pesquisa, cheguei à conclusão de que algo deve estar errado com a implementação do WaitForExit. Então eu examinei o código e encontrei esse comentário na implementação da Microsoft:
Aqui está a minha pergunta - e fiz um bom esforço de avaliação sem sucesso:
Como posso ter um tempo limite difícil no WaitForExit () e, ao mesmo tempo, certifique-se de que a captura de saída do console esteja completa?
Você tentou usar o evento OnExited em uma classe derivada? Veja isso. Isso pode permitir que você use tempo limite para esperar em vez de passá-lo para WaitForExit.
@Hans Passant Seu comentário me dirigiu na direção certa. Na verdade, eu realmente não sei quanto tempo para dormir (). Mas eu posso adicionar um cão de observação simples para isso. Uma vez que o processo já foi concluído no momento em que eu tenho que começar a aguardar as capturas perdidas, posso assumir que o evento OutputDataReceived é disparado continuamente até o fluxo estar vazio. Então, tudo o que fiz foi adicionar um Cronômetro e reiniciá-lo cada vez no início do método de evento OutputDataReceived. No código principal, eu substituo a chamada para WaitForExit (60000) pelo seguinte código:
Outputdatareceived after waitforexit
Obter através da App Store Leia esta publicação em nosso aplicativo!
Como ler para finalizar a saída do processo de forma assíncrona em C #?
Eu tenho problemas com a leitura da saída de um processo de forma assíncrona em C #. Eu encontrei algumas outras perguntas semelhantes neste site, mas eles realmente não me ajudam. Aqui está o que eu faço:
Criar novo processo Definir startinfo - FileName, Argumentos, CreateNoWindow (true), UseShellExecute (false), RedirectStandardOutput (true) Adicionar o manipulador de eventos para OutputDataReceived; Iniciar processo, BeginOutputReadLine e, em seguida, WaitForExit ().
Isso funciona bem, mas o resultado do processo iniciado grava alguns por cento (%) que eu quero obter, mas não posso desde o meu código ler linha a linha e os por cento não aparecem.
Aqui está o código atual do meu programa:
Parece que ler o fluxo de saída de forma assíncrona é um pouco quebrado - nem todos os dados são lidos antes do processo sair. Mesmo se você chamar Process. WaitForExit () e mesmo se você chamar Process. Close () (ou Dispose ()), você ainda pode obter muitos dados depois. Veja alabaxblog. info/2018/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ para um registro completo, mas a solução é basicamente usar métodos síncronos. Para evitar um impasse, você deve chamar um deles em outro tópico:
Process. WaitForExit () aguardará até que a saída assíncrona de saída / fluxo de leitura finalize. Infelizmente, isso não é verdade para a sobrecarga Process. WaitForExit (tempo limite). Isto é o que a classe Process faz internamente:
. Então, aguardará as leituras assíncronas somente se não houver tempo limite! Para corrigi-lo, basta chamar WaitForExit sem parâmetros () após WaitForExit (timeout) retornado true:
Há poucas coisas que estão ficando a caminho disso. O aplicativo de console provavelmente está usando o backspace "\ b" para substituir a porcentagem, talvez ele não esteja acendendo o fluxo de stdout após cada gravação, e BeginOutputReadLine presumivelmente espera o fim da linha antes de fornecer dados.
Herança múltipla.
Derivado de muitas coisas.
O processo Async OutputDataReceived / ErrorDataReceived tem uma falha ao lidar com prompts.
Classe System. Diagnostics. Process - Parte 2.
Na Parte 1, discuti o problema em que usando o redirecionamento stdout / stderr e lendo-os de forma síncrona, o buffer de saída pode ficar cheio e bloquear o processo filho na próxima "gravação". Em seguida, ocorre um impasse porque o processo pai está aguardando que a criança saia antes de ler dados do buffer, a criança está bloqueando a gravação porque o buffer está cheio e, portanto, o impasse. 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ágoa.
Não sou eu, é você.
Eu me deparo com outra questão, que parecia o mesmo impasse, onde uma má pressuposição na implementação da classe Processo da Microsoft causou um tipo diferente de impasse entre os processos pai e filho. Eu achei quando meu processo filho me levou a afirmar a atividade solicitada, veja a Figura 1 para um exemplo.
O prompt terminou sem uma nova linha, deixando o cursor no final do prompt (o que faz sentido, por que o cursor seria configurado sob o prompt e não ao lado dele na tela). Veja a Figura 2, por exemplo, o código desse prompt.
O meu processo pai foi codificado para inspecionar os dados de stdout recebidos de forma assíncrona, procurando o prompt. Se / quando o prompt foi recebido, o processo pai emitiria uma resposta ao stdin do processo filho. Veja a Figura 3.
No entanto, quando executo meu processo pai e o processo filho enviou o prompt, o texto nunca veio no evento OutputDataReceived ... e meu processo pai estava sentado na linha p. WaitForExit () para sempre ... O processo Child estava aguardando uma resposta que nunca veio dos pais, e estávamos mais uma vez em um impasse, desta vez eu não era eu ... O problema não estava no meu código.
No NewLine, no DataReceivedEvent ...
Comecei a depurar e assistir os eventos OutputDataReceived e comecei a mudar o código de processo filho de várias maneiras. A alteração pequena abaixo causou o evento OutputDataReceived para disparar com o prompt de texto, veja 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 um NewLine não dispararia o evento OutputDataReceived e, portanto, o processo pai não obteve 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 é meu código, mas um aplicativo de consola de terceiros, então, finalmente, eu precisava receber prompts com NewLines. Resolver o problema exigiu que eu implementei minha própria classe Async IO para ler o processo stdout / stderror.
Eu criei uma classe chamada StdStreamReader que lê um fluxo e desencadeia eventos basicamente semelhantes ao evento Processo OutputDataReceived, no entanto, ele não destrói uma NewLine para disparar um evento recebido de dados, 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 de processo Parent usando a nova classe de leitor.
O código para o StdStreamReader é bastante mundano, mas o resultado foi o esperado, veja a Figura 6.
Observe que "Sim" não foi exibido logo após o prompt - isso ocorre porque ele foi enviado diretamente para o processo filho stdin e não exibido.
Clique para o código fonte completo, que é fornecido "como está", sem garantia de qualquer tipo.
Compartilhar isso:
Relacionados.
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!
Agradeço um milhão, cara! Aparentemente, esta questão ainda é relevante após 4 anos. Me ajudou muito.
Flush Async Output Events.
A classe System. Diagnostics. Process fornece as opções para recuperar todas as saídas de um processo externo depois de concluído ou para recuperar eventos que contêm o resultado conforme ocorre a execução. A opção para receber eventos de saída assíncronos é útil nos cenários em que você precisa fornecer feedback dos usuários ou registrar a saída quando os tempos limite ocorrerem.
Ao receber eventos de saída assíncronos, você deve garantir que o processo externo tenha sido concluído quando você quiser garantir que você obtenha toda a saída. No entanto, esperar que HasExited seja verdade não é suficiente. Para garantir que todo o processamento tenha sido concluído, incluindo o tratamento de eventos de saída assíncronos, você deve chamar Process. WaitForExit (). Você precisa chamar a sobrecarga sem argumentos, mesmo se você já chamou a sobrecarga Process. WaitForExit (Int32). O Process. WaitForExit (Int32) docs indica:
Para garantir que o tratamento de eventos assíncrono tenha sido concluído, chame a sobrecarga WaitForExit () que não leva nenhum parâmetro depois de receber uma verdade dessa sobrecarga.
Abaixo está um exemplo simples que usa Process. WaitForExit (Int32) para impor tempo limite e Process. WaitForExit () para liberar os eventos de saída quando o processo for concluído.
Questões? Comentários? Comece uma conversa no Twitter.
Obter através da App Store Leia esta publicação em nosso aplicativo!
Como ler para finalizar a saída do processo de forma assíncrona em C #?
Eu tenho problemas com a leitura da saída de um processo de forma assíncrona em C #. Eu encontrei algumas outras perguntas semelhantes neste site, mas eles realmente não me ajudam. Aqui está o que eu faço:
Criar novo processo Definir startinfo - FileName, Argumentos, CreateNoWindow (true), UseShellExecute (false), RedirectStandardOutput (true) Adicionar o manipulador de eventos para OutputDataReceived; Iniciar processo, BeginOutputReadLine e, em seguida, WaitForExit ().
Isso funciona bem, mas o resultado do processo iniciado grava alguns por cento (%) que eu quero obter, mas não posso desde o meu código ler linha a linha e os por cento não aparecem.
Aqui está o código atual do meu programa:
Parece que ler o fluxo de saída de forma assíncrona é um pouco quebrado - nem todos os dados são lidos antes do processo sair. Mesmo se você chamar Process. WaitForExit () e mesmo se você chamar Process. Close () (ou Dispose ()), você ainda pode obter muitos dados depois. Veja alabaxblog. info/2018/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ para um registro completo, mas a solução é basicamente usar métodos síncronos. Para evitar um impasse, você deve chamar um deles em outro tópico:
Process. WaitForExit () aguardará até que a saída assíncrona de saída / fluxo de leitura finalize. Infelizmente, isso não é verdade para a sobrecarga Process. WaitForExit (tempo limite). Isto é o que a classe Process faz internamente:
. Então, aguardará as leituras assíncronas somente se não houver tempo limite! Para corrigi-lo, basta chamar WaitForExit sem parâmetros () após WaitForExit (timeout) retornado true:
Há poucas coisas que estão ficando a caminho disso. O aplicativo de console provavelmente está usando o backspace "\ b" para substituir a porcentagem, talvez ele não esteja acendendo o fluxo de stdout após cada gravação, e BeginOutputReadLine presumivelmente espera o fim da linha antes de fornecer dados.
Herança múltipla.
Derivado de muitas coisas.
O processo Async OutputDataReceived / ErrorDataReceived tem uma falha ao lidar com prompts.
Classe System. Diagnostics. Process - Parte 2.
Na Parte 1, discuti o problema em que usando o redirecionamento stdout / stderr e lendo-os de forma síncrona, o buffer de saída pode ficar cheio e bloquear o processo filho na próxima "gravação". Em seguida, ocorre um impasse porque o processo pai está aguardando que a criança saia antes de ler dados do buffer, a criança está bloqueando a gravação porque o buffer está cheio e, portanto, o impasse. 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ágoa.
Não sou eu, é você.
Eu me deparo com outra questão, que parecia o mesmo impasse, onde uma má pressuposição na implementação da classe Processo da Microsoft causou um tipo diferente de impasse entre os processos pai e filho. Eu achei quando meu processo filho me levou a afirmar a atividade solicitada, veja a Figura 1 para um exemplo.
O prompt terminou sem uma nova linha, deixando o cursor no final do prompt (o que faz sentido, por que o cursor seria configurado sob o prompt e não ao lado dele na tela). Veja a Figura 2, por exemplo, o código desse prompt.
O meu processo pai foi codificado para inspecionar os dados de stdout recebidos de forma assíncrona, procurando o prompt. Se / quando o prompt foi recebido, o processo pai emitiria uma resposta ao stdin do processo filho. Veja a Figura 3.
No entanto, quando executo meu processo pai e o processo filho enviou o prompt, o texto nunca veio no evento OutputDataReceived ... e meu processo pai estava sentado na linha p. WaitForExit () para sempre ... O processo Child estava aguardando uma resposta que nunca veio dos pais, e estávamos mais uma vez em um impasse, desta vez eu não era eu ... O problema não estava no meu código.
No NewLine, no DataReceivedEvent ...
Comecei a depurar e assistir os eventos OutputDataReceived e comecei a mudar o código de processo filho de várias maneiras. A alteração pequena abaixo causou o evento OutputDataReceived para disparar com o prompt de texto, veja 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 um NewLine não dispararia o evento OutputDataReceived e, portanto, o processo pai não obteve 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 é meu código, mas um aplicativo de consola de terceiros, então, finalmente, eu precisava receber prompts com NewLines. Resolver o problema exigiu que eu implementei minha própria classe Async IO para ler o processo stdout / stderror.
Eu criei uma classe chamada StdStreamReader que lê um fluxo e desencadeia eventos basicamente semelhantes ao evento Processo OutputDataReceived, no entanto, ele não destrói uma NewLine para disparar um evento recebido de dados, 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 de processo Parent usando a nova classe de leitor.
O código para o StdStreamReader é bastante mundano, mas o resultado foi o esperado, veja a Figura 6.
Observe que "Sim" não foi exibido logo após o prompt - isso ocorre porque ele foi enviado diretamente para o processo filho stdin e não exibido.
Clique para o código fonte completo, que é fornecido "como está", sem garantia de qualquer tipo.
Compartilhar isso:
Relacionados.
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!
Agradeço um milhão, cara! Aparentemente, esta questão ainda é relevante após 4 anos. Me ajudou muito.
Flush Async Output Events.
A classe System. Diagnostics. Process fornece as opções para recuperar todas as saídas de um processo externo depois de concluído ou para recuperar eventos que contêm o resultado conforme ocorre a execução. A opção para receber eventos de saída assíncronos é útil nos cenários em que você precisa fornecer feedback dos usuários ou registrar a saída quando os tempos limite ocorrerem.
Ao receber eventos de saída assíncronos, você deve garantir que o processo externo tenha sido concluído quando você quiser garantir que você obtenha toda a saída. No entanto, esperar que HasExited seja verdade não é suficiente. Para garantir que todo o processamento tenha sido concluído, incluindo o tratamento de eventos de saída assíncronos, você deve chamar Process. WaitForExit (). Você precisa chamar a sobrecarga sem argumentos, mesmo se você já chamou a sobrecarga Process. WaitForExit (Int32). O Process. WaitForExit (Int32) docs indica:
Para garantir que o tratamento de eventos assíncrono tenha sido concluído, chame a sobrecarga WaitForExit () que não leva nenhum parâmetro depois de receber uma verdade dessa sobrecarga.
Abaixo está um exemplo simples que usa Process. WaitForExit (Int32) para impor tempo limite e Process. WaitForExit () para liberar os eventos de saída quando o processo for concluído.
Questões? Comentários? Comece uma conversa no Twitter.
Comments
Post a Comment