Skip to content

Add Process.Start callback overload with ref struct ProcessStartArguments#128862

Draft
Copilot wants to merge 17 commits into
mainfrom
copilot/add-process-start-overload
Draft

Add Process.Start callback overload with ref struct ProcessStartArguments#128862
Copilot wants to merge 17 commits into
mainfrom
copilot/add-process-start-overload

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 1, 2026

main PR

Description

Adds Process.Start(ProcessStartInfo, Func<ProcessStartArguments, SafeProcessHandle>) to let callers create processes with custom platform APIs while still using Process infrastructure (ReadAllText, async stream handling, WaitForExit, etc.).

This PR also incorporates review feedback across the callback path and tests, including:

  • safer callback start-path handling on Unix and Windows
  • callback test coverage for both stdout and stderr content
  • Unix callback test gating for supported platforms
  • API/doc updates to match the finalized callback argument shape
  • Windows callback stderr assertion updated to match actual cmd /c echo ... 1>&2 output formatting ("error \r\n" with trailing space before \r\n)

Most notably, callback executable-path data and callback argument lifetime behavior were updated based on feedback:

  • ProcessStartArguments.FileName was replaced with Unix-only ProcessStartArguments.ResolvedPath
  • ResolvedPath is now byte* and annotated with [UnsupportedOSPlatform("windows")]
  • Unix callback startup now prepares a UTF-8 null-terminated path pointer without managed byte[] allocation
  • Unix callback test now consumes args.ResolvedPath directly for posix_spawn
  • ProcessStartArguments is now a ref struct to constrain usage to callback lifetime by construction

Additional Unix callback-path update from feedback:

  • resolved-path stack buffer sizing is now debug/release split (2 in DEBUG, 256 otherwise) to exercise both allocation paths in debug test runs
  • stack allocation now uses a const-size stackalloc buffer and slices to the required length

Customer Impact

Developers who need unsupported/native process creation mechanisms (for example custom CreateProcess*/posix_spawn flows) can still rely on .NET Process features instead of reimplementing process I/O and lifetime handling themselves.

Regression

No known product regression is being fixed. This is a new callback-based API path plus follow-up correctness and API-shape refinements from review feedback.

Testing

Validated with targeted builds in this repo:

  • ./dotnet.sh build src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj -c Release -v minimal /p:BuildProjectReferences=false
  • ./dotnet.sh build src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.csproj -c Release -v minimal /p:BuildProjectReferences=false

System.Diagnostics.Process.Tests full project build remains blocked in this environment by existing shared-framework prerequisite errors (eng/targetingpacks.targets), unrelated to these code changes.

Risk

Moderate. This change adds new public API surface and adjusts callback argument shape on Unix (ResolvedPath pointer) and callback-lifetime constraints (ProcessStartArguments as ref struct).
Risk is mitigated by:

  • focused changes in callback-specific paths
  • explicit API/ref/doc updates to keep surface consistent
  • targeted build validation and callback test updates

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

Copilot AI and others added 2 commits June 1, 2026 17:33
Introduces a new Process.Start(ProcessStartInfo, Func<ProcessStartArguments, SafeProcessHandle>)
overload that allows users to create processes using their own system calls while
leveraging .NET's Process infrastructure (pipe management, async I/O, WaitForExit).

- Added ProcessStartArguments class with platform-specific pointer properties
- Added Windows implementation using BuildCommandLine/GetEnvironmentVariablesBlock
- Added Unix implementation using ResolvePath/ParseArgv/AllocArgvArray/AllocEnvpArray
- Updated ref assembly with new public API surface
- Added Windows test using CreateProcess inside the callback
- Added Unix test using posix_spawn inside the callback

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-diagnostics-process
See info in area-owners.md if you want to be subscribed.

- reduce code duplication
- acquire the locks!
- fix Windows tests: enable inheritance
- fix Unix implementation: handle "usesTerminal"
@adamsitnik adamsitnik force-pushed the copilot/add-process-start-overload branch from 638b886 to 3163021 Compare June 2, 2026 10:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new public Process.Start overload that prepares stdio handles + argument/environment buffers and then delegates the actual OS process creation to a user callback, returning a Process wired up to existing Process infrastructure (streams, WaitForExit, etc.).

Changes:

  • Adds new public API surface: Process.Start(ProcessStartInfo, Func<ProcessStartArguments, SafeProcessHandle>) and ProcessStartArguments.
  • Extends Windows/Unix start paths and SafeProcessHandle to support invoking a callback-based start flow.
  • Adds Windows + Unix tests exercising callback-based process creation and output redirection.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Windows.cs Adds a Windows test using CreateProcess inside the callback to validate stdout redirection.
src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Unix.cs Adds a Unix test using posix_spawn inside the callback to validate stdout redirection.
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartArguments.cs Introduces the new argument carrier type passed to the callback.
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs Threads optional callback through the Windows StartCore flow.
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs Threads optional callback through the Unix StartCore flow and updates wait-state ownership logic.
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs Adds the new public Process.Start callback overload and refactors internal start flow.
src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj Adds the new ProcessStartArguments.cs to compilation.
src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx Adds a resource string for invalid callback handle errors.
src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs Adds Windows callback start helper that builds/pins command line + env block and enables handle inheritance.
src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs Adds Unix callback start helper that allocates argv/envp and integrates wait-state registration.
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.csproj Enables unsafe blocks for the ref assembly build due to pointer-typed API.
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs Updates ref surface with new public Process.Start overload and ProcessStartArguments.
src/libraries/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs Promotes argv/envp allocation helpers to internal for reuse.

Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please address my feedback

Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Unix.cs Outdated
Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Unix.cs Outdated
Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Unix.cs Outdated
Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Unix.cs Outdated
Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Windows.cs Outdated
Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Windows.cs Outdated
Copilot AI and others added 2 commits June 2, 2026 11:01
…local const, redirect stderr

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot June 2, 2026 11:03
Copilot AI requested a review from adamsitnik June 2, 2026 11:04
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 2, 2026 14:43
Copilot AI requested review from Copilot and removed request for Copilot June 2, 2026 17:59
Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address my feedback

Comment on lines +53 to +54
byte[] pathBytes = System.Text.Encoding.UTF8.GetBytes(args.FileName! + '\0');
fixed (byte* pathPtr = pathBytes)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The users don't need to perform any kind of encoding/decoding for other parameters like args or env vars, they should not need to do it for FileName as well.

@copilot Since FileName is used only on Unix, we should do the following:

  • rename FileName to ResolvedPath
  • change the type of the property from string? to byte*
  • annotate the property with [UnsupportedOSPlatform("windows")]
  • update SafeProcessHandle.Unix.cs to provide us with pinned byte*, also ensure the encoding is done without allocating any managed memory

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3079bdd. ProcessStartArguments.FileName is now ResolvedPath (byte*, [UnsupportedOSPlatform("windows")]), Unix start now provides the UTF-8 null-terminated pointer without managed byte[] allocation, and the Unix callback test now uses args.ResolvedPath directly.

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot June 2, 2026 18:43
Copilot AI requested a review from adamsitnik June 2, 2026 18:44
public bool StandardError { get { throw null; } }
public override string ToString() { throw null; }
}
public sealed partial class ProcessStartArguments
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to consider: Should this be ref struct to limit the lifetime to the callback by construction?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds very reasonable. But this would force us to introduce a dedicated delegate type?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - if we wanted to pass the structs as in.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please give this idea a try (make it ref struct, but don't introduce a dedicated delegate if you don't have to), but separate it from other changes (use dedicated commit)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in d30a6f62: ProcessStartArguments is now a ref struct, and I kept this as a dedicated commit separate from other changes.

Copy link
Copy Markdown
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please address my feedback

public bool StandardError { get { throw null; } }
public override string ToString() { throw null; }
}
public sealed partial class ProcessStartArguments
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please give this idea a try (make it ref struct, but don't introduce a dedicated delegate if you don't have to), but separate it from other changes (use dedicated commit)

Comment thread src/libraries/System.Diagnostics.Process/tests/ProcessHandlesTests.Windows.cs Outdated
Copilot AI and others added 2 commits June 3, 2026 11:03
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot June 3, 2026 11:04
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 3, 2026

@copilot please address my feedback

Addressed in two commits: 26a38000 (Unix resolved-path buffer feedback) and d30a6f62 (ProcessStartArguments as ref struct in a dedicated commit).

Copilot AI changed the title Add Process.Start callback overload with ProcessStartArguments Add Process.Start callback overload with ref struct ProcessStartArguments Jun 3, 2026
Copilot AI requested a review from adamsitnik June 3, 2026 11:06
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot June 3, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants