J'ai le code suivant:
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
Je sais que la sortie du processus que je commence est d'environ 7 Mo de long. L'exécuter dans la console Windows fonctionne correctement. Malheureusement, par programme, cela se bloque indéfiniment à WaitForExit. Notez également que le code ne se bloque PAS pour les sorties plus petites (comme 3 Ko).
Est-il possible que le StandardOutput interne dans ProcessStartInfo ne puisse pas mettre en mémoire tampon 7 Mo? Si oui, que dois-je faire à la place? Sinon, qu'est-ce que je fais de mal?
c#
processstartinfo
Epaga
la source
la source
Réponses:
Le problème est que si vous redirigez
StandardOutput
et / ouStandardError
la mémoire tampon interne peut devenir pleine. Quel que soit l'ordre que vous utilisez, il peut y avoir un problème:StandardOutput
termine avant de lire, le processus peut bloquer toute tentative d’écriture, de sorte que le processus ne se termine jamais.StandardOutput
aide de ReadToEnd, votre processus peut se bloquer si le processus ne se ferme jamaisStandardOutput
(par exemple s'il ne se termine jamais, ou s'il est bloqué en écritureStandardError
).La solution consiste à utiliser des lectures asynchrones pour garantir que la mémoire tampon ne soit pas pleine. Pour éviter les blocages et collecter toutes les sorties des deux
StandardOutput
etStandardError
vous pouvez le faire:EDIT: Voir les réponses ci-dessous pour savoir comment éviter une ObjectDisposedException si le délai d'expiration se produit.
la source
using
instructions pour les gestionnaires d'événements doivent être au - dessus de l'using
instruction pour le processus lui-même?La documentation pour
Process.StandardOutput
dit à lire avant d'attendre sinon vous pouvez bloquer, extrait de code copié ci-dessous:la source
RedirectStandardOutput = true;
et que vous ne l'utilisez pas,p.StandardOutput.ReadToEnd();
vous obtenez un blocage / blocage.La réponse de Mark Byers est excellente, mais je voudrais simplement ajouter ce qui suit:
Les délégués
OutputDataReceived
etErrorDataReceived
doivent être supprimés avantoutputWaitHandle
eterrorWaitHandle
être supprimés. Si le processus continue à produire des données après le dépassement du délai d'attente, puis se termine, les variablesoutputWaitHandle
eterrorWaitHandle
seront accessibles après avoir été supprimées.(Pour info, je devais ajouter cette mise en garde comme réponse car je ne pouvais pas commenter son message.)
la source
Il s'agit d'une solution plus moderne attendue, basée sur la bibliothèque parallèle de tâches (TPL) pour .NET 4.5 et supérieur.
Exemple d'utilisation
la mise en oeuvre
la source
Le problème avec ObjectDisposedException non gérée se produit lorsque le processus est expiré. Dans ce cas, les autres parties de la condition:
ne sont pas exécutés. J'ai résolu ce problème de la manière suivante:
la source
output
eterror
àoutputBuilder
? Quelqu'un peut-il s'il vous plaît fournir une réponse complète qui fonctionne?Rob y a répondu et m'a épargné encore quelques heures d'essais. Lisez le tampon de sortie / d'erreur avant d'attendre:
la source
WaitForExit()
?ReadToEnd
ou des méthodes similaires (commeStandardOutput.BaseStream.CopyTo
) reviendra après TOUTES les données sont lues. rien ne viendra aprèsNous avons également ce problème (ou une variante).
Essayez ce qui suit:
1) Ajoutez un délai à p.WaitForExit (nnnn); où nnnn est en millisecondes.
2) Placez l'appel ReadToEnd avant l'appel WaitForExit. C'est ce que nous avons vu MS recommander.
la source
Crédit à EM0 pour https://stackoverflow.com/a/17600012/4151626
Les autres solutions (y compris EM0) sont toujours bloquées pour mon application, en raison de délais d'expiration internes et de l'utilisation de StandardOutput et StandardError par l'application générée. Voici ce qui a fonctionné pour moi:
Edit: ajout de l'initialisation de StartInfo à l'exemple de code
la source
Je l'ai résolu de cette façon:
J'ai redirigé les entrées, les sorties et les erreurs et j'ai géré la lecture à partir des flux de sortie et d'erreur. Cette solution fonctionne pour le SDK 7- 8.1, à la fois pour Windows 7 et Windows 8
la source
J'ai essayé de créer une classe qui résoudrait votre problème en utilisant la lecture de flux asynchrone, en tenant compte des réponses de Mark Byers, Rob, stevejay. Ce faisant, j'ai réalisé qu'il y avait un bogue lié à la lecture du flux de sortie du processus asynchrone.
J'ai signalé ce bogue chez Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/3119134
Résumé:
Vous feriez probablement mieux d'utiliser la lecture asynchrone comme suggéré par d'autres utilisateurs pour votre cas. Mais vous devez être conscient que vous pourriez manquer certaines informations en raison de conditions de course.
la source
Je pense que c'est une approche simple et meilleure (nous n'avons pas besoin
AutoResetEvent
):la source
.FileName = Path + @"\ggsci.exe" + @" < obeycommand.txt"
simplifier votre code? Ou peut-être quelque chose d'équivalent"echo command | " + Path + @"\ggsci.exe"
si vous ne voulez vraiment pas utiliser un fichier obeycommand.txt séparé.Aucune des réponses ci-dessus ne fait le travail.
La solution Rob se bloque et la solution 'Mark Byers' obtient l'exception supprimée (j'ai essayé les «solutions» des autres réponses).
J'ai donc décidé de proposer une autre solution:
Ce code débogué et fonctionne parfaitement.
la source
GetProcessOutputWithTimeout
method.introduction
La réponse actuellement acceptée ne fonctionne pas (lance une exception) et il y a trop de solutions de contournement mais pas de code complet. Cela fait évidemment perdre beaucoup de temps aux gens parce que c'est une question populaire.
En combinant la réponse de Mark Byers et la réponse de Karol Tyl, j'ai écrit le code complet en fonction de la manière dont je souhaite utiliser la méthode Process.Start.
Usage
Je l'ai utilisé pour créer une boîte de dialogue de progression autour des commandes git. Voici comment je l'ai utilisé:
En théorie, vous pouvez également combiner stdout et stderr, mais je n'ai pas testé cela.
Code
la source
Je sais que c'est vieux souper mais, après avoir lu cette page entière, aucune des solutions ne fonctionnait pour moi, même si je n'ai pas essayé Muhammad Rehan car le code était un peu difficile à suivre, même si je suppose qu'il était sur la bonne voie . Quand je dis que cela n'a pas fonctionné, ce n'est pas tout à fait vrai, parfois cela fonctionnait bien, je suppose que c'est quelque chose à voir avec la longueur de la sortie avant une marque EOF.
Quoi qu'il en soit, la solution qui a fonctionné pour moi était d'utiliser différents threads pour lire StandardOutput et StandardError et écrire les messages.
J'espère que cela aide quelqu'un, qui pensait que cela pourrait être si difficile!
la source
sw.FlushAsync(): Object is not set to an instance of an object. sw is null.
comment / où doit-sw
on définir?Après avoir lu tous les articles ici, je me suis installé sur la solution consolidée de Marko Avlijaš. Cependant , cela n'a pas résolu tous mes problèmes.
Dans notre environnement, nous avons un service Windows qui est programmé pour exécuter des centaines de fichiers .bat .cmd .exe, ... etc. différents qui se sont accumulés au fil des ans et ont été écrits par de nombreuses personnes et dans des styles différents. Nous n'avons aucun contrôle sur l'écriture des programmes et des scripts, nous sommes simplement responsables de la planification, de l'exécution et des rapports sur les succès / échecs.
J'ai donc essayé à peu près toutes les suggestions ici avec différents niveaux de succès. La réponse de Marko était presque parfaite, mais lorsqu'elle était exécutée en tant que service, elle ne capturait pas toujours stdout. Je n'ai jamais compris pourquoi pas.
La seule solution que nous avons trouvée qui fonctionne dans TOUS nos cas est la suivante: http://csharptest.net/319/using-the-processrunner-class/index.html
la source
Solution de contournement que j'ai fini par utiliser pour éviter toute la complexité:
Je crée donc un fichier temporaire, redirige à la fois la sortie et l'erreur vers celui-ci en utilisant
> outputfile > 2>&1
, puis je lis simplement le fichier une fois le processus terminé.Les autres solutions conviennent pour les scénarios où vous souhaitez faire d'autres choses avec la sortie, mais pour des choses simples, cela évite beaucoup de complexité.
la source
J'ai lu de nombreuses réponses et créé les miennes. Je ne suis pas sûr que celui-ci résoudra dans tous les cas, mais il corrige dans mon environnement. Je n'utilise tout simplement pas WaitForExit et j'utilise WaitHandle.WaitAll sur les signaux de sortie et de fin d'erreur. Je serai heureux, si quelqu'un voit des problèmes possibles avec cela. Ou si cela aidera quelqu'un. Pour moi, c'est mieux car n'utilise pas les délais d'attente.
la source
Je pense qu'avec async, il est possible d'avoir une solution plus élégante et de ne pas avoir de blocages même en utilisant à la fois standardOutput et standardError:
Il est basé sur la réponse de Mark Byers. Si vous n'êtes pas dans une méthode asynchrone, vous pouvez utiliser à la
string output = tStandardOutput.result;
place deawait
la source
Aucune de ces réponses ne m'a aidé, mais cette solution fonctionnait bien avec la gestion des blocages
https://stackoverflow.com/a/60355879/10522960
la source
Ce message est peut-être obsolète, mais j'ai découvert que la principale raison pour laquelle il se bloque généralement est due à un débordement de pile pour le redirectStandardoutput ou si vous avez redirectStandarderror.
Comme les données de sortie ou les données d'erreur sont volumineuses, cela entraînera un temps de blocage car il est toujours en cours de traitement pour une durée indéfinie.
donc pour résoudre ce problème:
la source
Appelons l'exemple de code publié ici le redirecteur et l'autre programme le redirigé. Si c'était moi, j'écrirais probablement un programme de test redirigé qui peut être utilisé pour dupliquer le problème.
Alors je l'ai fait. Pour les données de test, j'ai utilisé le PDF de spécification du langage ECMA-334 C #; c'est environ 5 Mo. Ce qui suit est la partie importante de cela.
La valeur de la taille de données ne correspond pas à la taille réelle du fichier, mais cela n'a pas d'importance. Il n'est pas clair si un fichier PDF utilise toujours CR et LF à la fin des lignes, mais cela n'a pas d'importance pour cela. Vous pouvez utiliser n'importe quel autre fichier texte volumineux pour tester.
En utilisant cela, l'exemple de code de redirecteur se bloque lorsque j'écris la grande quantité de données, mais pas lorsque j'écris une petite quantité.
J'ai beaucoup essayé de retracer l'exécution de ce code et je n'ai pas pu. J'ai commenté les lignes du programme redirigé qui ont désactivé la création d'une console pour le programme redirigé pour essayer d'obtenir une fenêtre de console séparée, mais je ne pouvais pas.
Ensuite, j'ai trouvé Comment démarrer une application console dans une nouvelle fenêtre, la fenêtre du parent ou aucune fenêtre . Donc, apparemment, nous ne pouvons pas (facilement) avoir une console séparée lorsqu'un programme de console démarre un autre programme de console sans ShellExecute et puisque ShellExecute ne prend pas en charge la redirection, nous devons partager une console, même si nous ne spécifions aucune fenêtre pour l'autre processus.
Je suppose que si le programme redirigé remplit une mémoire tampon quelque part, il doit attendre que les données soient lues et si, à ce stade, aucune donnée n'est lue par le redirecteur, il s'agit d'un blocage.
La solution est de ne pas utiliser ReadToEnd et de lire les données pendant l'écriture des données, mais il n'est pas nécessaire d'utiliser des lectures asynchrones. La solution peut être assez simple. Ce qui suit fonctionne pour moi avec le PDF de 5 Mo.
Une autre possibilité est d'utiliser un programme GUI pour effectuer la redirection. Le code précédent fonctionne dans une application WPF, sauf avec des modifications évidentes.
la source
J'avais le même problème, mais la raison était différente. Cela se produirait cependant sous Windows 8, mais pas sous Windows 7. La ligne suivante semble avoir causé le problème.
La solution était de NE PAS désactiver UseShellExecute. J'ai maintenant reçu une fenêtre contextuelle Shell, ce qui est indésirable, mais bien mieux que le programme qui n'attend rien de particulier. J'ai donc ajouté la solution de contournement suivante pour cela:
Maintenant, la seule chose qui me dérange est de savoir pourquoi cela se produit sous Windows 8 en premier lieu.
la source
UseShellExecute
être défini sur false si vous souhaitez rediriger la sortie.