Monday 14 May 2018

Process outputdatareceived 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.
Os eventos Process. WaitForExit e. Exited não estão funcionando?
. NET System. Diagnostics. Process Class & # 8211; Parte 1.
Os eventos Process. WaitForExit e. Exited não estão funcionando? Eu pensei que tinha achado que este era o caso - mas foi minha culpa, provavelmente o mesmo para você também. Analisarei o que encontrei enquanto explorava e resolvia esse problema.
Resposta curta: Se você estiver redirecionando StandardOutput e / ou StandardError, use melhor os métodos assíncronos Process. BeginErrorReadLine () e. BeginOutputReadLine () ANTES de chamar. WaitForExit () e capture a saída enganchando os eventos Process. ErrorDataReceived e. OutputDataReceived.
A resposta longa começa comigo usando o arquivo Visual Studio diffmerge. exe na pasta Common7 / IDE para comparar arquivos de texto no modo em lotes. Eu estou introduzindo um teste de regressão em um processo em lote iniciado pela construção. Eu precisava de uma ferramenta que cuspisse um arquivo de diferenças de texto ao comparar dois arquivos (não um arquivo de resultado de mesclagem). O WinMerge e o Beyond Compare estão à minha disposição, mas eles não parecem produzir nada além de resultados mesclados (que normalmente é o que eu quero, mas não dessa vez).
Meu framework de regressão irá chamar diffmerge. exe e armazenar o arquivo diff resultante para revisão posterior. Eu codifiquei meu ProcessStartInfo…
Seguiu-se com o início do processo e aguardando que o processo terminasse ... e esperando ... espere esperando ....
Isso me fez ler o MSDN e aprofundar o uso da classe Process. Eu descobri algumas informações interessantes, provavelmente deveria ter sido óbvio.
Primeiro, descobri que, às vezes, executar meu processo filho do diffmerge com argumentos diferentes funcionava, às vezes não, o que tornava o assunto misterioso.
Em segundo lugar, descobri que funcionava bem quando eu não redirecionava a saída.
Então, obviamente, eu estava perdendo alguma coisa. Eu precisava ler os documentos da Process API e, assim, encontrei este nugget: Artigo do MSDN.
Depois de encontrar e ler esse artigo do MSDN eu entendi. Meu exemplo de código acima funcionará se o buffer StdOut ou StdError não estiver cheio. No entanto, o que eu estava vendo foi o buffer StdOut sendo preenchido, o processo filho foi bloqueado na próxima gravação StdOut / StdError, o processo pai estava aguardando infinitamente no processo filho para sair antes de ler o buffer StdOut / StdError. Para mim, parece que o método WaitForExit e / ou o evento Exited estão quebrados / não estão pegando o processo filho, mas foi meu código que foi quebrado.
Eu modifiquei o código para usar os métodos assíncronos e, de repente, meus problemas desapareceram. Não há mais bloqueio, tudo funcionou como esperado.
Eu usei StringBuilders como buffers para armazenar os dados recebidos nos eventos.
Na parte 2, me deparo com um problema com as implementações de ReadLine do processo StdOut / StdError em torno de minhas necessidades específicas, entro em como resolvi esse problema.

Process outputdatareceived waitforexit
Eu escrevi um formulário que cria processos e redireciona de forma assíncrona a saída padrão para um StringBuilder. O principal problema é que, de vez em quando, meu processo não é liberado corretamente, fazendo com que o criador de string fique incompleto.
Todas as respostas.
bool bExit = process. WaitForExit (50000);
que (eu acho) encerra o processo após 50 segundos, se ele não sair sozinho. Se isso estiver correto, a correção é:
bool bExit = process. WaitForExit ();
o que faria com que o programa esperasse para sempre até o final do processo - nesse caso, ele seria liberado corretamente. Se você não quiser esperar para sempre, tente aumentar a quantidade de tempo que você espera para 10 minutos:
bool bExit = process. WaitForExit (600000);
Ou mude o processo sample_huge_output. exe para que ele saia corretamente se for encerrado durante a execução e liberado corretamente.
Espero que isso funcione e aproveite a codificação.
Sugerido como resposta por CountryStyle terça-feira, 17 de maio de 2011 18:04 Unproposed by answer by dagabe14 terça-feira, 17 de maio de 2011 18:39.
Obrigado pela resposta. Infelizmente, isso não responde à minha pergunta. O principal objetivo deste código é uma ferramenta automatizada para classificar c & # 43; & # 43; atribuições para um curso que eu ensino. Eu poderia ter ido com um script simples como a maioria dos instrutores, mas eu achei um formulário c # melhor para que eu pudesse torná-lo o mais genérico possível.
O que isso significa é que nem sempre sei quanto tempo o processo levará e se o processo sairá corretamente, devido ao fato de que, se um aluno enviar uma tarefa que contenha um loop infinito ou algo semelhante, esse CreateProcess função está em deadlock, desde que o processo nunca terminará.

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.

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.

Process outputdatareceived waitforexit
Eu estou tentando iniciar um processo e pegue o StandardOutput e StandardError usando EventHandlers e BeginOutputReadLine. Eu posso iniciar o processo, mas recebo.
Exceção chamando & quot; BeginOutputReadLine & quot; com & quot; 0 & quot; argumento (s): & quot; Não é possível misturar operação síncrona e assíncrona no fluxo do processo. & quot;
Aqui está a minha função de lançamento:
Estou violando o modelo de segmentação Powershell? Obrigado!
Eu estou tentando iniciar um processo e pegue o StandardOutput e StandardError usando EventHandlers e BeginOutputReadLine. Eu posso iniciar o processo, mas recebo.
Exceção chamando & quot; BeginOutputReadLine & quot; com & quot; 0 & quot; argumento (s): & quot; Não é possível misturar operação síncrona e assíncrona no fluxo do processo. & quot;
A mensagem de erro está dizendo o que você está fazendo errado.
Você não pode atribuir funções a um processo síncrono. As funções não são delegadas, portanto, não funcionarão. Você precisa usar um modelo de evento ou usar a variação de asynch.
Use isso como um modelo:
Marcado como Resposta Oliver Lipkau Moderator quarta-feira, 12 de outubro de 2011 2:32.
Todas as respostas.
Eu acredito que você tenha alguns erros no seu código. Eu sou a pessoa mais humilde que você já conheceu.
Eu estou tentando iniciar um processo e pegue o StandardOutput e StandardError usando EventHandlers e BeginOutputReadLine. Eu posso iniciar o processo, mas recebo.
Exceção chamando & quot; BeginOutputReadLine & quot; com & quot; 0 & quot; argumento (s): & quot; Não é possível misturar operação síncrona e assíncrona no fluxo do processo. & quot;
A mensagem de erro está dizendo o que você está fazendo errado.
Você não pode atribuir funções a um processo síncrono. As funções não são delegadas, portanto, não funcionarão. Você precisa usar um modelo de evento ou usar a variação de asynch.
Use isso como um modelo:
Marcado como Resposta Oliver Lipkau Moderator quarta-feira, 12 de outubro de 2011 2:32.
Ok, eu não entendi a implementação do BeginOutputReadline. Eu pensei que isso funcionaria com o. WaitForExit - olhando para ele agora, vejo por que isso não acontece.
Em VB, eu sei como 1) gerar threads para lidar com isso e eu sei que você pode colocar o processo em um loop 2) até que seja concluído como uma maneira de lidar com isso.
Eu não quero fazer # 2. Existe uma maneira multithreaded ou assíncrona para ler a saída de um determinado processo como o processo está gerando?
Ok, eu não entendi a implementação do BeginOutputReadline. Eu pensei que isso funcionaria com o. WaitForExit - olhando para ele agora, vejo por que isso não acontece.
Em VB, eu sei como 1) gerar threads para lidar com isso e eu sei que você pode colocar o processo em um loop 2) até que seja concluído como uma maneira de lidar com isso.
Eu não quero fazer # 2. Existe uma maneira multithreaded ou assíncrona para ler a saída de um determinado processo como o processo está gerando?
Na verdade, a menos que você queira criar um delegado de evento. Mas então este é o PowerShell, não é VB.
Você pode começar aqui e ver se o evento que você está procurando pode ser "viciado"
help Register-ObjectEvent - full.
A resposta marcada não resolve o problema com o qual o OP está lutando - isto é, redirecionando StdErr e StdOut e sofre com o bug de conflito discutido aqui:
& quot; Uma condição de deadlock pode resultar se o processo pai chama p. WaitForExit antes de p. StandardOutput. ReadToEnd e o processo filho grava texto suficiente para preencher o fluxo redirecionado. O processo pai aguardaria indefinidamente que o processo filho fosse encerrado. O processo filho esperaria indefinidamente que o pai lesse o fluxo StandardOutput completo. & Quot;
Eu também estou tentando a melhor resposta para fazer este trabalho no Powershell, mas todos os exemplos de código que estou executando em que usam System. Diagnostics. Process no powersehll estão replicando o deadlock nesta implementação ou não estão redirecionando stderr e stdout.
A solução que estou usando usa o cmdlet Start-Process da seguinte maneira:
Não é uma ótima solução, pois só redirecionará stdout e stderr para um arquivo de texto. mas funcionará o tempo todo e redirecionará stdout e stderr.
Se formos usar saída redirecionada, não usaremos espera. Esse foi o design de CreateProcess desde o NT4.
O uso de arquivos para capturar a saída funcionará porque um fluxo é anexado e envia uma leitura constante para o fluxo de saída. O fluxo de saída nunca é suspenso. Yu não pode fazer isso em um loop de código. O córrego corre o contexto do sistema do pecado eu acredito e começarei sempre a possibilidade de ler. A espera está no seu thread, então você não pode fazer uma leitura manual. É um impasse como afirmado.
O uso da leitura do console é geralmente porque estamos procurando por um prompt. Nesse caso, nunca esperamos a conclusão. O código ZTHe provavelmente será enviado para 'quit' e nós iremos verificar o processo e girá-lo até que ele seja desligado.
A questão aqui é que não podemos gerar um thead. Em VB ou C #, C nós apenas usamos threads separe para IO e um para espera. Melhor ainda, eu usaria um semáforo como ele pode capturar dinamicamente um desligamento inesperado.
Talvez a próxima versão do PowerSHell tenha boas possibilidades de multithreading.
Esta é apenas mais uma lição de que o Powershell é uma linguagem de script, não uma linguagem de programação e, por mais poderosa que seja, existem armadilhas.
O código de exemplo da Microsoft simplesmente não é abrangente o suficiente para um hacker Powerhell perceber que eles não podem chegar lá a partir daqui. Isso é o que me pegou e acredito no OP também, porque houve uma certa expectativa de que BeginOutputReadline implementou a mágica para fazer com que funcionasse threads que teriam resolvido o problema de bloqueio. Não, então acabamos aqui.
E uma ligeira correcção ao meu código - deixei o sinalizador - wait que é necessário para o resto do código funcionar:
Esta é apenas mais uma lição de que o Powershell é uma linguagem de script, não uma linguagem de programação e, por mais poderosa que seja, existem armadilhas.
O código de exemplo da Microsoft simplesmente não é abrangente o suficiente para um hacker Powerhell perceber que eles não podem chegar lá a partir daqui. Isso é o que me pegou e acredito no OP também, porque houve uma certa expectativa de que BeginOutputReadline implementou a mágica para fazer com que funcionasse threads que teriam resolvido o problema de bloqueio. Não, então acabamos aqui.
E uma ligeira correcção ao meu código - deixei o sinalizador - wait que é necessário para o resto do código funcionar:
Certo. O PowerShell é propositadamente limitado para torná-lo mais seguro para a população em geral e para reduzir os problemas que ocorrem com as linguagens compiladas.
Sim - o sinalizador de espera ajudaria, pois o arquivo pode não ter terminado o spool antes de o programa ser encerrado.
O Start-Process é um wrapper fraco na API do processo, mas protege os desavisados ​​dos deadlocks porque você não pode redirecionar a entrada, exceto de um arquivo.
Você não precisa de esperar para redirecionar. Você precisa esperar pelos arquivos e como uma maneira fácil de detectar a terminação. Usando a API, isso tem que ser feito consultando o objeto de processo ou extraindo a saída e aguardando que o processo envie sua mensagem de término e então gire o status.
Isso é tudo demais para os administradores de scripts e, como você apontou, os deixará sem problemas.

No comments:

Post a Comment