Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
244 views
in Technique[技术] by (71.8m points)

c# - Why does StandardOutput.Read() block when StartInfo.RedirectStandardInput is set to true?

I'm having a hard time deciphering the MSDN doc about Process.StandardOutpout as to if the Read(Char[], Int32, Int32) method blocks or not. My understanding is that it shouldn't block, but it seems like it does when I set RedirectStandardInput to true.

Does anybody have experience with this; or some explanation for the issue I'm having?

The context here is that I don't want to wait for a full line (ie with a line terminator), or for the process to exit before reading standard output. Also I don't want to use callbacks. I want to read StdOut synchronously as the process writes to it.

Here is a simplified version of my code:

string command = @"C:flex_sdksflex_sdk_4.5.1.21328infcsh.exe";
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = false; # <-- if I set this to true, then 
                                           # the program hangs on
                                           # p.StandardOutput.Read later on
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = command;
p.Start();

StringBuilder sb_stdout = new StringBuilder(1024);
char[] buffer = new char[64];
int nb_bytes_read;
while (true) {
    do {
        nb_bytes_read = p.StandardOutput.Read(buffer, 0, buffer.Length);
        sb_stdout.Append(new string(buffer, 0, nb_bytes_read));
    } while (nb_bytes_read > 0);
    if (sb_stdout.ToString().EndsWith("
(fcsh) "))
        break;
    Thread.Sleep(20);
}

Update

Based on my (probably bad) assumption that Process.StandardOutput is broken when used:

  • with stdin redirected; and,
  • while reading something else than terminated lines from stdout or stderr,

I decided to try using Windows' API directly. I added an answer with such code; it works fine (at least for now).

Another update

I created a blog entry with the code I now use.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

i fought and fought with this just last week actually... for some reason anything other than the Read() call (ReadToEnd() was not what i needed) seemed to block and never return. here's what i did to finally get it to "work":

snip 1:

    private bool ThreadExited = true;
    private bool ExitThread = false;
    private void ReadThread()
    {
        while (!ExitThread)
        {
            string Output = "";

            int CharacterInt = myProcess.StandardOutput.Read();

            while (CharacterInt > 0)
            {
                char Character = (char)CharacterInt;
                Output += Character;

                var MyDelegate = new delegateUpdateText(UpdateText);
                Invoke(MyDelegate, Output);

                Output = "";
                CharacterInt = myProcess.StandardOutput.Read();
            }

            System.Threading.Thread.Yield();
        }

        ThreadExited = true;
    }

snip 2:

    private void InitializeProcess()
    {
        ThreadExited = true;
        ExitThread = true;

        while (!ThreadExited)
            System.Threading.Thread.Sleep(1000);

        ThreadExited = false;
        ExitThread = false;

        myProcess = new Process();

        ProcessStartInfo PSI = myProcess.StartInfo;

        PSI.FileName = @"cmd.exe";
        PSI.UseShellExecute = false;
        PSI.RedirectStandardError = false;
        PSI.RedirectStandardInput = true;
        PSI.RedirectStandardOutput = true;
        PSI.CreateNoWindow = false;
        PSI.ErrorDialog = true;


        myProcess.StartInfo = PSI;

        myProcess.Exited += new EventHandler(myProcess_Exited);

        myProcess.EnableRaisingEvents = false;


        myProcess.Start();

        ReadThreadThread = new System.Threading.Thread(ReadThread);
        ReadThreadThread.Start();
    }
    private System.Threading.Thread ReadThreadThread;

that finally ended up working for me. in my case i was writing text to to a textbox but that should be easy enough to modify to something else. but anything else i did caused issues due to the blocks; for some reason even if i used reflection to get the number of bytes that were available, calling the ReadBlock() function would block. never did figure it out to my satisfaction.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...