C #: Sortie de l’application de la console de redirection: Comment vider la sortie?

Je crée une application de console externe et utilise la redirection de sortie asynchrone.
comme indiqué dans ce post SO

Mon problème est qu’il semble que le processus engendré ait besoin de produire une certaine quantité de sortie avant que je reçoive la notification d’événement OutputDataReceived .

Je souhaite recevoir l’événement OutputDataReceived dès que possible.

J’ai une application de redirection de base, et voici quelques observations:
1. Quand j’appelle un simple ‘tant que (vrai) print (“X”);’ application console (C #) Je reçois immédiatement l’événement de sortie. 2. Lorsque j’appelle une application de partie 3D, j’essaie de terminer à partir de la ligne de commande, je vois la sortie ligne par ligne.
3. Lorsque j’appelle cette application de partie 3D depuis mon wrapper avec os nu (voir 1), la sortie est fournie en morceaux (environ une page).

Que se passe-t-il dans cette application?

FYI: L’application en question est un “USBee DX Data Exctarctor (bus async) v1.0”.

J’ai fait quelques recherches et un correctif à la classe de processus microsofts. Mais comme ma dernière réponse a été supprimée sans raison, j’ai dû en créer une nouvelle.

Alors, prenez cet exemple …

Créez une application Windows et collez une zone de texte riche sur le formulaire principal, puis ajoutez-la au chargement du formulaire …

Process p = new Process() { StartInfo = new ProcessStartInfo() { FileName = "cmd.exe", CreateNoWindow = true, UseShellExecute = false, ErrorDialog = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, }, EnableRaisingEvents = true, SynchronizingObject = this }; p.OutputDataReceived += (s, ea) => this.richTextBox1.AppendText(ea.Data); p.Start(); p.BeginOutputReadLine(); 

Cela produira quelque chose comme ça …

 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. 

L’événement OutputDataReceived n’est pas déclenché pour la dernière ligne. Après quelques tentatives ILS, il semble que cela soit délibéré, car la dernière ligne ne se termine pas par un fichier crlf; elle suppose qu’il ya plus de messages à venir et l’ajoute au début de l’événement suivant.

Pour corriger cela, j’ai écrit un wrapper pour la classe Process et en ai extrait certaines des classes internes requirejses afin que tout fonctionne correctement. Voici la classe FixedProcess …

 using System; using System.Collections; using System.IO; using System.Text; using System.Threading; namespace System.Diagnostics { internal delegate void UserCallBack(ssortingng data); public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e); public class FixedProcess : Process { internal AsyncStreamReader output; internal AsyncStreamReader error; public event DataReceivedEventHandler OutputDataReceived; public event DataReceivedEventHandler ErrorDataReceived; public new void BeginOutputReadLine() { Stream baseStream = StandardOutput.BaseStream; this.output = new AsyncStreamReader(this, baseStream, new UserCallBack(this.FixedOutputReadNotifyUser), StandardOutput.CurrentEncoding); this.output.BeginReadLine(); } public void BeginErrorReadLine() { Stream baseStream = StandardError.BaseStream; this.error = new AsyncStreamReader(this, baseStream, new UserCallBack(this.FixedErrorReadNotifyUser), StandardError.CurrentEncoding); this.error.BeginReadLine(); } internal void FixedOutputReadNotifyUser(ssortingng data) { DataReceivedEventHandler outputDataReceived = this.OutputDataReceived; if (outputDataReceived != null) { DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data); if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) { this.SynchronizingObject.Invoke(outputDataReceived, new object[] { this, dataReceivedEventArgs }); return; } outputDataReceived(this, dataReceivedEventArgs); } } internal void FixedErrorReadNotifyUser(ssortingng data) { DataReceivedEventHandler errorDataReceived = this.ErrorDataReceived; if (errorDataReceived != null) { DataReceivedEventArgs dataReceivedEventArgs = new DataReceivedEventArgs(data); if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) { this.SynchronizingObject.Invoke(errorDataReceived, new object[] { this, dataReceivedEventArgs }); return; } errorDataReceived(this, dataReceivedEventArgs); } } } internal class AsyncStreamReader : IDisposable { internal const int DefaultBufferSize = 1024; private const int MinBufferSize = 128; private Stream stream; private Encoding encoding; private Decoder decoder; private byte[] byteBuffer; private char[] charBuffer; private int _maxCharsPerBuffer; private Process process; private UserCallBack userCallBack; private bool cancelOperation; private ManualResetEvent eofEvent; private Queue messageQueue; private SsortingngBuilder sb; private bool bLastCarriageReturn; public virtual Encoding CurrentEncoding { get { return this.encoding; } } public virtual Stream BaseStream { get { return this.stream; } } internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding) : this(process, stream, callback, encoding, 1024) { } internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) { this.Init(process, stream, callback, encoding, bufferSize); this.messageQueue = new Queue(); } private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize) { this.process = process; this.stream = stream; this.encoding = encoding; this.userCallBack = callback; this.decoder = encoding.GetDecoder(); if (bufferSize < 128) { bufferSize = 128; } this.byteBuffer = new byte[bufferSize]; this._maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize); this.charBuffer = new char[this._maxCharsPerBuffer]; this.cancelOperation = false; this.eofEvent = new ManualResetEvent(false); this.sb = null; this.bLastCarriageReturn = false; } public virtual void Close() { this.Dispose(true); } void IDisposable.Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing && this.stream != null) { this.stream.Close(); } if (this.stream != null) { this.stream = null; this.encoding = null; this.decoder = null; this.byteBuffer = null; this.charBuffer = null; } if (this.eofEvent != null) { this.eofEvent.Close(); this.eofEvent = null; } } internal void BeginReadLine() { if (this.cancelOperation) { this.cancelOperation = false; } if (this.sb == null) { this.sb = new StringBuilder(1024); this.stream.BeginRead(this.byteBuffer, 0, this.byteBuffer.Length, new AsyncCallback(this.ReadBuffer), null); return; } this.FlushMessageQueue(); } internal void CancelOperation() { this.cancelOperation = true; } private void ReadBuffer(IAsyncResult ar) { int num; try { num = this.stream.EndRead(ar); } catch (IOException) { num = 0; } catch (OperationCanceledException) { num = 0; } if (num == 0) { lock (this.messageQueue) { if (this.sb.Length != 0) { this.messageQueue.Enqueue(this.sb.ToString()); this.sb.Length = 0; } this.messageQueue.Enqueue(null); } try { this.FlushMessageQueue(); return; } finally { this.eofEvent.Set(); } } int chars = this.decoder.GetChars(this.byteBuffer, 0, num, this.charBuffer, 0); this.sb.Append(this.charBuffer, 0, chars); this.GetLinesFromStringBuilder(); this.stream.BeginRead(this.byteBuffer, 0, this.byteBuffer.Length, new AsyncCallback(this.ReadBuffer), null); } private void GetLinesFromStringBuilder() { int i = 0; int num = 0; int length = this.sb.Length; if (this.bLastCarriageReturn && length > 0 && this.sb[0] == '\n') { i = 1; num = 1; this.bLastCarriageReturn = false; } while (i < length) { char c = this.sb[i]; if (c == '\r' || c == '\n') { if (c == '\r' && i + 1 < length && this.sb[i + 1] == '\n') { i++; } string obj = this.sb.ToString(num, i + 1 - num); num = i + 1; lock (this.messageQueue) { this.messageQueue.Enqueue(obj); } } i++; } // Flush Fix: Send Whatever is left in the buffer string endOfBuffer = this.sb.ToString(num, length - num); lock (this.messageQueue) { this.messageQueue.Enqueue(endOfBuffer); num = length; } // End Flush Fix if (this.sb[length - 1] == '\r') { this.bLastCarriageReturn = true; } if (num < length) { this.sb.Remove(0, num); } else { this.sb.Length = 0; } this.FlushMessageQueue(); } private void FlushMessageQueue() { while (this.messageQueue.Count > 0) { lock (this.messageQueue) { if (this.messageQueue.Count > 0) { ssortingng data = (ssortingng)this.messageQueue.Dequeue(); if (!this.cancelOperation) { this.userCallBack(data); } } continue; } break; } } internal void WaitUtilEOF() { if (this.eofEvent != null) { this.eofEvent.WaitOne(); this.eofEvent.Close(); this.eofEvent = null; } } } public class DataReceivedEventArgs : EventArgs { internal ssortingng _data; /// Gets the line of characters that was written to a redirected  output stream. /// The line that was written by an associated  to its redirected  or  stream. /// 2 public ssortingng Data { get { return this._data; } } internal DataReceivedEventArgs(ssortingng data) { this._data = data; } } } 

Collez-le dans votre projet et changez ensuite …

 Process p = new Process() { .... 

à

 FixedProcess p = new FixedProcess() { .... 

Maintenant, votre application devrait afficher quelque chose comme ça …

 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Projects\FixedProcess\bin\Debug> 

sans avoir besoin d’apporter d’autres modifications à votre code existant. Il est également toujours asynchrone et bien emballé. La mise en garde est que maintenant vous aurez plusieurs événements pour une sortie importante avec des interruptions potentielles entre les deux, vous devrez donc gérer ce scénario vous-même. À part ça, ça devrait être tout bon.

Découvrez cette réponse.

Comment envoyer une entrée à la console comme si l’utilisateur tapait?

L’idée est que vous recevrez les événements de sortie reçus lorsque l’un d’entre eux est lancé après le démarrage du processus.

Il semble que le problème réside dans le fait que l’application factice a été écrite en c #, ce qui vide automatiquement la sortie une fois à l’impression tandis que l’application tierce est écrite en c / c ++ et n’est donc écrite que lorsque le stdoutbuffer est plein. La seule solution que ive ait trouvée est de s’assurer que l’application c / c ++ est vidée après chaque impression ou de définir son tampon sur 0.