December 5th, 2025
mind blownlike3 reactions

How can my process read its own standard output?

A customer wanted to know how their C# program could read its own standard output. They tried this:

// Get my process
Process p = Process.GetCurrentProcess();

// Make sure my standard output is not redirected
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = false;

// Now read it -- fails
string output = p.StandardOutput.ReadToEnd();

They got an exception saying, “StandardOut has not been redirected or the process hasn’t started yet.”

The Standard­Output property is valid only after you call Process.Start. Specifically, if you call Process.Start when Redirect­Standard­Output is set to true the CLR creates a pipe and connects the write end of the pipe to the child’s standard output and the read end of the pipe to the Process.Standard­Output property. If you never call Process.Start, or you did not enable standard output redirection, then the Process.Standard­Output property will not be set and will not work.

In my opinion, putting properties on the Process object that are meaningful only for started processes was a mistake. Those properties should have been put on a new class like Process­Start­Result. That way, it’s clear that the only way to get them is to start a process.

If you are a C# process, you could call Console.SetOut() to replace the standard output with a pipe you created.¹ However, if anybody read the Console.Out property and saved it in a variable, then they would still be operating on the original standard output stream and not your replacement.

The most reliable solution is to relaunch yourself as a child process with redirected standard output and have the parent read from the resulting Process.StandardOutput stream while the child writes to it.

¹ This is the CLR analog to the Win32 function Set­Std­Handle(). Note that Win32 and the CLR have separate bookkeeping for this. The CLR initializes its standard output from the Win32 standard output, but the two can diverge afterward because the CLR does not bother to keep its value in sync with Win32 or vice versa.

Note also that the analogy continues, because the analogous solution for Win32 console programs is to call Set­Std­Handle, but you have the same risk that somebody has already read the original standard handle and saved it away. And as we saw last time, that risk is a reality because the C runtime library does exactly that. The analogous functions to Console.SetOut() for C are those in the freopen family.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

11 comments

Discussion is closed. Login to edit/delete existing comments.

Sort by :
  • Igor Levicki · Edited

    @Kevin Norris

    The main problem is actually that .Net Framework architects used the same Process struct for both input and output which is bad idea since presence of properties can imply usage modes that aren't possible to those developers unfamiliar with Windows API inner workings which nowadays is most of them.

    Raymond's proposal of Process and ProcessResult is reasonable enough and not over-engineered like what you proposed.

    You could still use single Process instance to start multiiple processes without calling it Template as long as outputs were separate.

    Another fundamental issue with .Net framework from the very beginning to version 10.0 is that it...

    Read more
  • Hamed

    Finally some C# stuff 😀

    • Igor Levicki

      Be careful what you are wishing for, soon you will be pleading for mercy and asking for C to return and bring sanity.

  • Igor Levicki · Edited

    Raymond, my condolences.

    If someone asked me that question, I’d probably be less patient.

    Maybe I am misunderstanding, but…

    WHY would you want to read your OWN standard output?

    I mean, aren’t YOU the one producing said output? If so, then why not just copy the string you just Console.WriteLine’d?!?

    Trying to read your own STDOUT is like writing a note to yourself, throwing it out the window, then running down 30 flights of stairs to catch it and read it again.

    I swear, people are dumb as rocks today.

    • Raymond ChenMicrosoft employee Author

      Maybe they are calling into a library that prints its results to stdout with no option to return the results as a string. So you have to capture that library’s stdout.

      • Igor Levicki · Edited

        Writing or using a library that outputs to STDOUT is already a huge red flag (libraries should not assume they have STDOUT to begin with), unless it is a library that manipulates output (by adding formatting, colors, etc) meant for use in console applications only which doesn't seem like the case here.

        Oh and by the way:

        > In my opinion, putting properties on the Process object that are meaningful only for started processes was a mistake.

        I totally agree on this and I'd buy you a beer and high-five you if I could. But that's .Net developers we are talking about...

        Read more
    • Bwmat

      Yeah, I was about to ask something similar; I don’t think it’s _possible_ to read from your own process’ stderr since that’s (usually?) a write-only pipe/file/whatever?

  • GL

    Setting StartInfo.UseShellExecute for the current process is pretty humorous because that would imply one can change how a process *was* started after it has started.

    • Neil Rashbrook

      So what should the API look like? My first guess would be a base class Process which contains most of the process operations, then a derived class ChildProcess which is created using a factory method taking an extended StartInfo object, which then allows you to access the standard input/output/error.

      • Michael Taylor

        The problem with adding more types is that the surface area, and therefore complexity, of the API increases. APIs are generally built for the most common use cases and, ideally, have some lower level things for specialized cases. Adding 2 or 3 extra types to handle the, probably uncommon, case of reading one's own input is overkill. It increases the surface area, adds complexity for a specialized case and requires maintenance of code that is rarely used.

        I would tend to agree that separating the pre-started process info from the started/terminated process would have been better. However you often need the...

        Read more
      • Kevin Norris

        There are at least three cases that ought to be distinguished. ProcessTemplate for a process that has not yet started (you could use the same configuration to spawn multiple processes, hence "Template"), Process for a process that has started (and possibly finished), and DeadProcess for a process that has definitely finished (the return type of some method like WaitForChildProcessToFinish(), but probably with a shorter name). We don't have a class for "definitely running and not finished," because that can change out from under you at any time, so it would not make sense as a class invariant.

        DeadProcess should be a...

        Read more