2009-06-23 17 views
5

मैं बाहरी कंसोल एप्लिकेशन को बढ़ा रहा हूं और async आउटपुट रीडायरेक्ट का उपयोग कर रहा हूं।
as shown in this SO postसी #: पुनर्निर्देशन कंसोल अनुप्रयोग आउटपुट: आउटपुट कैसे फ्लश करें?

मेरे समस्या ऐसा लगता है कि पैदा की प्रक्रिया से पहले मैं OutputDataReceived घटना सूचना मिलती है तो उत्पादन की निश्चित राशि का उत्पादन करने की जरूरत है।

मैं आउटपुटडेटा जितनी जल्दी हो सके प्राप्त करना चाहता हूं।

मेरे पास एक नंगे हड्डियों को पुनर्निर्देशित करने वाला एप्लिकेशन है, और यहां कुछ अवलोकन हैं:
1. जब मैं एक सरल 'सत्य (सत्य) प्रिंट ("एक्स") कहता हूं;' कंसोल एप्लिकेशन (सी #) मैं तुरंत आउटपुट घटना प्राप्त करता हूं। 2. जब मैं एक 3 डी पार्टी ऐप कहता हूं तो मैं को कमांड लाइन से लपेटने की कोशिश कर रहा हूं, मुझे लाइन-दर-लाइन आउटपुट दिखाई देता है।
3. जब मैं अपने 3 डी पार्टी ऐप को अपने नंगे-हड्डी रैपर से कॉल करता हूं (देखें 1) - आउटपुट भाग में आता है (लगभग एक पेज आकार)।

उस ऐप के अंदर क्या होता है?

एफवाईआई: प्रश्न में ऐप "यूएसबी डीएक्स डेटा एक्सक्टेक्टर (असिनक बस) v1.0" है।

उत्तर

8

मैंने कुछ और शोध किया और माइक्रोस्कोफ्ट प्रोसेस क्लास को ठीक किया। लेकिन जैसा कि मेरा आखिरी उत्तर किसी कारण के बिना हटा दिया गया था, मुझे एक नया निर्माण करना पड़ा।

तो इस उदाहरण

 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(); 

हो जाएगा ताकि उत्पादन कुछ ले ...

एक विंडोज़ अनुप्रयोग बनाएँ और मुख्य प्रपत्र पर एक अमीर पाठ बॉक्स छड़ी, तो प्रपत्र लोड करने के लिए इस जोड़ें ... इस तरह ...

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

आउटपुटडेटा प्राप्त कार्यक्रम अंतिम पंक्ति के लिए नहीं निकाल दिया गया है। कुछ ILSpying के बाद ऐसा लगता है कि यह जानबूझकर है क्योंकि आखिरी पंक्ति एक सीआरएलएफ के साथ खत्म नहीं होती है, ऐसा लगता है कि और अधिक आ रहा है और इसे अगले कार्यक्रम की शुरुआत में जोड़ता है।

इसे ठीक करने के लिए, मैंने प्रोसेस क्लास के लिए एक रैपर लिखा है और इसमें से कुछ आवश्यक आंतरिक कक्षाएं ली हैं ताकि यह सब अच्छी तरह से काम कर सके। यहां फिक्स्ड प्रोसेस क्लास है ...

using System; 
using System.Collections; 
using System.IO; 
using System.Text; 
using System.Threading; 

namespace System.Diagnostics 
{ 
    internal delegate void UserCallBack(string 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(string 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(string 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 StringBuilder 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) 
        { 
         string data = (string)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 string _data; 
     /// <summary>Gets the line of characters that was written to a redirected <see cref="T:System.Diagnostics.Process" /> output stream.</summary> 
     /// <returns>The line that was written by an associated <see cref="T:System.Diagnostics.Process" /> to its redirected <see cref="P:System.Diagnostics.Process.StandardOutput" /> or <see cref="P:System.Diagnostics.Process.StandardError" /> stream.</returns> 
     /// <filterpriority>2</filterpriority> 
     public string Data 
     { 
      get 
      { 
       return this._data; 
      } 
     } 
     internal DataReceivedEventArgs(string data) 
     { 
      this._data = data; 
     } 
    } 
} 

अपनी परियोजना में चिपकाएं और फिर बदलें ...

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

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

को अब अपने आवेदन कुछ इस तरह प्रदर्शित करना चाहिए ...

Microsoft Windows [Version 6.1.7601] 

Copyright (c) 2009 Microsoft Corporation. All rights reserved. 

C:\Projects\FixedProcess\bin\Debug> 

अपने मौजूदा कोड में कोई भी अन्य परिवर्तन करने के लिए की जरूरत के बिना। यह अभी भी async है और अच्छी तरह से लपेटा है। एक चेतावनी यह है कि अब आप बड़े आउटपुट के लिए संभावित ब्रेक के साथ कई कार्यक्रम प्राप्त करेंगे, इसलिए आपको इस परिदृश्य को स्वयं संभालना होगा। इसके अलावा, यह सब अच्छा होना चाहिए।

+0

"एक चेतावनी यह है कि अब आप के बीच संभावित ब्रेक के साथ बड़े आउटपुट के लिए कई कार्यक्रम मिलेंगे" - यह वास्तव में एक समस्या होगी? मैंने अभी 'आउटपुटडेटा रिसेसिव' ईवेंट में 'स्ट्रिंगबिल्डर। ऐपेंड() 'जोड़ा है। – Nyerguds

+0

यह विधि कभी-कभी मुझे लघु कार्यक्रम चलाने पर खाली आउटपुट देती है, बीटीडब्ल्यू ... क्या अंतिम मूल्य को संसाधित करने से पहले एसिंक प्रक्रिया पूरी तरह से पकड़ने का इंतजार करने का कोई तरीका है? – Nyerguds

+0

उस नोट पर, क्या यह एसिंक ऑपरेशन की प्रतीक्षा करने के लिए 'Exited 'ईवेंट को ओवरराइड नहीं करना चाहिए? – Nyerguds

1

इस उत्तर को देखें।

How to send input to the console as if the user is typing?

विचार जब किसी भी के बाद प्रक्रिया शुरू कर दी है फेंक दिया जाता है आप उत्पादन प्राप्त घटनाओं प्राप्त होगा कि है।

1

ऐसा लगता है कि समस्या यह थी कि डमी ऐप सी # में लिखा गया था जो आउटपुट को स्वचालित रूप से प्रत्येक प्रिंटल में फिसलता है जबकि तृतीय पक्ष ऐप सी/सी ++ में लिखा गया था और इसलिए केवल स्टडीउटबफर भरने पर ही लिखा गया था। यह पता लगाने के लिए एकमात्र समाधान यह है कि सी/सी ++ ऐप प्रत्येक प्रिंट के बाद फ्लश करता है या 0 बफर को 0 बजे सेट करने के लिए