<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Process API | The .NET Blog</title><link>https://thedotnetblog.com/tags/process-api/</link><description>Articles, tutorials and insights from the .NET community.</description><generator>Hugo</generator><language>en</language><managingEditor>@thedotnetblog (The .NET Blog)</managingEditor><webMaster>@thedotnetblog</webMaster><lastBuildDate>Tue, 26 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://thedotnetblog.com/tags/process-api/index.xml" rel="self" type="application/rss+xml"/><item><title>.NET 11 Finally Fixes the Process API</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/dotnet-11-process-api-improvements-runandcapturetext/</link><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/dotnet-11-process-api-improvements-runandcapturetext/</guid><description>System.Diagnostics.Process gets its biggest update in years. RunAndCaptureTextAsync, KillOnParentExit, SafeProcessHandle APIs, and full control over standard handle redirection — no more deadlock boilerplate.</description><content:encoded>&lt;p&gt;Every .NET developer who has ever needed to spawn a process and capture its output has written some variation of the same dangerous boilerplate: async read from stdout, async read from stderr, &lt;code&gt;WaitForExitAsync&lt;/code&gt;, don&amp;rsquo;t forget to drain both streams or you&amp;rsquo;ll deadlock. It&amp;rsquo;s a well-known trap that&amp;rsquo;s been there for years.&lt;/p&gt;
&lt;p&gt;.NET 11 finally fixes this properly.&lt;/p&gt;
&lt;h2 id="runandcapturetextasync"&gt;RunAndCaptureTextAsync&lt;/h2&gt;
&lt;p&gt;The headline addition: a single static method that starts a process, captures stdout and stderr, and waits for exit without deadlocking.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunAndCaptureTextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;dotnet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One call. No manual stream draining. No carefully positioned &lt;code&gt;WaitForExit&lt;/code&gt;. If you just need to run something and get its output, this is the API you want.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also &lt;code&gt;Process.RunAsync&lt;/code&gt; for the case where you want to wait for exit without capturing output.&lt;/p&gt;
&lt;h2 id="killonparentexit"&gt;KillOnParentExit&lt;/h2&gt;
&lt;p&gt;A common problem with spawned processes: if the parent crashes or is killed, child processes keep running as orphans. &lt;code&gt;KillOnParentExit&lt;/code&gt; lets you declare at process start time that the child process should be terminated when the parent process exits.&lt;/p&gt;
&lt;p&gt;This is a feature that&amp;rsquo;s existed in platform-specific ways (job objects on Windows, prctl on Linux) but required p/invoke or third-party libraries to use from .NET. Now it&amp;rsquo;s a first-class property on &lt;code&gt;ProcessStartInfo&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="safeprocesshandle-based-apis"&gt;SafeProcessHandle-Based APIs&lt;/h2&gt;
&lt;p&gt;The new lightweight API surface is built around &lt;code&gt;SafeProcessHandle&lt;/code&gt; rather than the full &lt;code&gt;Process&lt;/code&gt; class. The full &lt;code&gt;Process&lt;/code&gt; class carries a lot of state and is difficult to trim — the &lt;code&gt;SafeProcessHandle&lt;/code&gt; path is more trimmer-friendly for applications that need to minimize output size (WASM, native AOT).&lt;/p&gt;
&lt;h2 id="full-control-over-handle-inheritance"&gt;Full Control Over Handle Inheritance&lt;/h2&gt;
&lt;p&gt;The update also adds fine-grained control over which handles a child process inherits and how standard handles are redirected. Previously you could redirect stdin/stdout/stderr but couldn&amp;rsquo;t specify exactly which handles to inherit at the OS level. The new APIs expose that control.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Process&lt;/code&gt; class is used in tooling, build systems, test runners, and any application that shells out to other executables. The old API surface dated back to .NET Framework and was showing its age. This isn&amp;rsquo;t a breaking change — the old APIs still work — but new code should prefer the new surface.&lt;/p&gt;
&lt;p&gt;For trimmed applications or AOT compilation scenarios, the &lt;code&gt;SafeProcessHandle&lt;/code&gt; path is particularly welcome. The old &lt;code&gt;Process&lt;/code&gt; class brought in a lot of reflection-heavy code that complicated trimming.&lt;/p&gt;
&lt;p&gt;Original post: &lt;a href="https://devblogs.microsoft.com/dotnet/process-api-improvements-in-dotnet-11/"&gt;Process API Improvements in .NET 11&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>