A hypothetical redesign of System.Diagnostics.Process to avoid confusion over properties that are valid only when you are the one who called Start

The Old New Thing (Raymond Chen) News

Summary

A blog post proposing a redesign of the System.Diagnostics.Process class in .NET to separate properties valid only for started processes into a new class, aiming to reduce API confusion.

<p>Some time ago, I noted that <a title="How can my process read its own standard output?" href="https://devblogs.microsoft.com/oldnewthing/20251205-00/?p=111843"> the <code>Process.<wbr />Standard­Output</code> property is an attractive nuisance</a> because it is valid only on <code>Process</code> objects that you called <code>Start</code> on. You can&#8217;t just grab any old <code>Process</code> object and try to access its standard handles.</p> <p>Others in the comments had their ideas on how to remove the confusion. Here&#8217;s mine. The principle is that the properties and methods of the <code>Process</code> object should be valid for all instances of the <code>Process</code> class. If a property or method is valid only conditionally, then either move it to a place that is accessible only if the condition is met, or get rid of it entirely if it adds no value.</p> <p>The standard handles are the three properties that make sense only for <code>Process</code> objects that were created by the static <code>Start</code> method. There are also four methods related to those standard handles, as well as two events. Move them all to a new class, call it <code>Process­<!-- -->Start­Result</code>:</p> <pre>class Process<!-- -->StartResult { public Process Process { get; } public System.IO.StreamWriter StandardInput { get; } public System.IO.StreamWriter StandardOutput { get; } public System.IO.StreamWriter StandardError { get; } public void BeginOutputReadLine(); public void CancelOutputReadLine(); public event DataReceivedEventHandler? OutputDataReceived; public void BeginErrorReadLine(); public void CancelErrorReadLine(); public event DataReceivedEventHandler? ErrorDataReceived; } </pre> <p>Change the signature of all the overloads of the <code>Start</code> method so that they return a <code>Process­<!-- -->Start­Result</code> instead of a <code>Process</code>. Now it is impossible to do anything with the standard handles from a process you didn&#8217;t start: If you didn&#8217;t start the process, then you don&#8217;t have a <code>Process­<!-- -->Start­Result</code>. This removes the confusion that existed in the original attempt to have a process read from its own standard output.</p> <p>This follows <a title="How do I design a class so that methods must be called in a certain order?" href="https://devblogs.microsoft.com/oldnewthing/20190318-00/?p=102324"> a principle I wrote about earlier</a>: To force the developer to do things in a certain order, make the second step dependent on something produced by the first step. In this case, we want to force the developer to call <code>Start</code> before they use the standard handles, so we put the members related to the standard handles on a thing that you can obtain only by calling <code>Start</code>.</p> <p>Next, remove the <code>Start­Info</code> property entirely. It serves two purposes:</p> <ul> <li>Prior to calling the <code>Start</code> method, it provides a convenient pre-made <code>Process­<!-- -->Start­Info</code>.</li> <li>After calling the <code>Start</code> method, it holds a copy of the parameters that you passed to the <code>Start</code> method.</li> </ul> <p>The first purpose is just to cover for people who are too lazy to write the <code>new</code> keyword. So don&#8217;t be lazy. Write <code>new Process­<!-- -->Start­Info()</code>.</p> <p>The second purpose doesn&#8217;t tell you anything you don&#8217;t already know, since you are the one who passed the parameters to the <code>Start</code> method in the first place. If they are so important to you, you can save them yourself.</p> <p>Removing the <code>Start­Info</code> avoids confusion over whether the properties in it describe the process you want to start, or whether they describe a process that has already started. (And often, it describes neither!)</p> <p>I think that takes care of the largest source of confusion over the proper use of the <code>Process</code> class.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260525-00/?p=112351">A hypothetical redesign of &lt;CODE&gt;System.&lt;WBR&gt;Diagnostics.&lt;WBR&gt;Process&lt;/CODE&gt; to avoid confusion over properties that are valid only when you are the one who called &lt;CODE&gt;Start&lt;/CODE&gt;</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 05/26/26, 08:50 AM

# A hypothetical redesign of System.Diagnostics.Process to avoid confusion over properties that are valid only when you are the one who called Start - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260525-00?p=112351](https://devblogs.microsoft.com/oldnewthing/20260525-00?p=112351) Some time ago, I noted that[the`Process\.Standard­Output`property is an attractive nuisance](https://devblogs.microsoft.com/oldnewthing/20251205-00/?p=111843)because it is valid only on`Process`objects that you called`Start`on\. You can’t just grab any old`Process`object and try to access its standard handles\. Others in the comments had their ideas on how to remove the confusion\. Here’s mine\. The principle is that the properties and methods of the`Process`object should be valid for all instances of the`Process`class\. If a property or method is valid only conditionally, then either move it to a place that is accessible only if the condition is met, or get rid of it entirely if it adds no value\. The standard handles are the three properties that make sense only for`Process`objects that were created by the static`Start`method\. There are also four methods related to those standard handles, as well as two events\. Move them all to a new class, call it`Process­Start­Result`: ``` class ProcessStartResult { public Process Process { get; } public System.IO.StreamWriter StandardInput { get; } public System.IO.StreamWriter StandardOutput { get; } public System.IO.StreamWriter StandardError { get; } public void BeginOutputReadLine(); public void CancelOutputReadLine(); public event DataReceivedEventHandler? OutputDataReceived; public void BeginErrorReadLine(); public void CancelErrorReadLine(); public event DataReceivedEventHandler? ErrorDataReceived; } ``` Change the signature of all the overloads of the`Start`method so that they return a`Process­Start­Result`instead of a`Process`\. Now it is impossible to do anything with the standard handles from a process you didn’t start: If you didn’t start the process, then you don’t have a`Process­Start­Result`\. This removes the confusion that existed in the original attempt to have a process read from its own standard output\. This follows[a principle I wrote about earlier](https://devblogs.microsoft.com/oldnewthing/20190318-00/?p=102324): To force the developer to do things in a certain order, make the second step dependent on something produced by the first step\. In this case, we want to force the developer to call`Start`before they use the standard handles, so we put the members related to the standard handles on a thing that you can obtain only by calling`Start`\. Next, remove the`Start­Info`property entirely\. It serves two purposes: - Prior to calling the`Start`method, it provides a convenient pre\-made`Process­Start­Info`\. - After calling the`Start`method, it holds a copy of the parameters that you passed to the`Start`method\. The first purpose is just to cover for people who are too lazy to write the`new`keyword\. So don’t be lazy\. Write`new Process­Start­Info\(\)`\. The second purpose doesn’t tell you anything you don’t already know, since you are the one who passed the parameters to the`Start`method in the first place\. If they are so important to you, you can save them yourself\. Removing the`Start­Info`avoids confusion over whether the properties in it describe the process you want to start, or whether they describe a process that has already started\. \(And often, it describes neither\!\) I think that takes care of the largest source of confusion over the proper use of the`Process`class\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) 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\.

Similar Articles

Understanding the rationale behind a rule when trying to circumvent it

The Old New Thing (Raymond Chen)

This article from Microsoft's Old New Thing blog explains the rationale behind best practices for Windows kernel callback functions, particularly why blocking or waiting on work items defeats their purpose, using a cautionary tale about drivers causing system hangs.

Why not have changes in API behavior depend on the SDK you link against?

The Old New Thing (Raymond Chen)

The article examines the pitfalls of altering API behavior depending on the linked SDK version, using Windows' CoInitializeSecurity as a case study. It discusses issues with DLL version mismatches and tail call optimization that complicate this approach.

Writing a Debugger from Scratch

Hacker News Top

This article kicks off a series on building a debugger from scratch in Rust, starting with attaching to a Windows process using OS debugging APIs.

Improving C# Memory Safety

Hacker News Top

Microsoft announces a redesign of C#'s unsafe keyword in C# 16 to enforce memory safety contracts, making unsafe operations visible and compiler-enforced, with preview in .NET 11 and production in .NET 12.