{"id":111843,"date":"2025-12-05T07:00:00","date_gmt":"2025-12-05T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111843"},"modified":"2025-12-05T09:46:07","modified_gmt":"2025-12-05T17:46:07","slug":"20251205-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251205-00\/?p=111843","title":{"rendered":"How can my process read its own standard output?"},"content":{"rendered":"<p>A customer wanted to know how their C# program could read its own standard output. They tried this:<\/p>\n<pre>\/\/ Get my process\r\nProcess p = Process.GetCurrentProcess();\r\n\r\n\/\/ Make sure my standard output is not redirected\r\np.StartInfo.UseShellExecute = false;\r\np.StartInfo.RedirectStandardOutput = false;\r\n\r\n\/\/ Now read it -- fails\r\nstring output = p.StandardOutput.ReadToEnd();\r\n<\/pre>\n<p>They got an exception saying, &#8220;StandardOut has not been redirected or the process hasn&#8217;t started yet.&#8221;<\/p>\n<p>The <code>Standard\u00adOutput<\/code> property is valid only after you call <code>Process.Start<\/code>. Specifically, if you call <code>Process.Start<\/code> when <code>Redirect\u00adStandard\u00adOutput<\/code> is set to <code>true<\/code> the CLR creates a pipe and connects the write end of the pipe to the child&#8217;s standard output and the read end of the pipe to the <code>Process.<wbr \/>Standard\u00adOutput<\/code> property. If you never call <code>Process.<wbr \/>Start<\/code>, or you did not enable standard output redirection, then the <code>Process.<wbr \/>Standard\u00adOutput<\/code> property will not be set and will not work.<\/p>\n<p>In my opinion, putting properties on the <code>Process<\/code> object that are meaningful only for started processes was a mistake. Those properties should have been put on a new class like <code>Process\u00adStart\u00adResult<\/code>. That way, it&#8217;s clear that the only way to get them is to start a process.<\/p>\n<p>If you are a C# process, you could call <code>Console.<wbr \/>SetOut()<\/code> to replace the standard output with a pipe you created.\u00b9 However, if anybody read the <code>Console.<wbr \/>Out<\/code> property and saved it in a variable, then they would still be operating on the original standard output stream and not your replacement.<\/p>\n<p>The most reliable solution is to relaunch yourself as a child process with redirected standard output and have the parent read from the resulting <code>Process.<wbr \/>StandardOutput<\/code> stream while the child writes to it.<\/p>\n<p>\u00b9 This is the CLR analog to the Win32 function <code>Set\u00adStd\u00adHandle()<\/code>. 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.<\/p>\n<p>Note also that the analogy continues, because the analogous solution for Win32 console programs is to call <code>Set\u00adStd\u00adHandle<\/code>, 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 <a title=\"How can I read the standard output of an already-running process?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251204-00\/?p=111841\"> the C runtime library does exactly that<\/a>. The analogous functions to <code>Console.<wbr \/>SetOut()<\/code> for C are those in the <code>freopen<\/code> family.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You&#8217;ll have to trick yourself before anybody notices, which may not be possible.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111843","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You&#8217;ll have to trick yourself before anybody notices, which may not be possible.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111843"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111843\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}