Add Process.Start callback overload with WindowsProcessStartArguments and UnixProcessStartArguments#128862
Add Process.Start callback overload with WindowsProcessStartArguments and UnixProcessStartArguments#128862Copilot wants to merge 26 commits into
Conversation
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>
- reduce code duplication - acquire the locks! - fix Windows tests: enable inheritance - fix Unix implementation: handle "usesTerminal"
638b886 to
3163021
Compare
There was a problem hiding this comment.
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>)andProcessStartArguments. - Extends Windows/Unix start paths and
SafeProcessHandleto 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. |
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
…local const, redirect stderr Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot fix following build failure:
src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs(237,70): error CS9080: (NETCORE_ENGINEERING_TELEMETRY=Build) Use of variable 'resolvedPathBuffer' in this context may expose referenced variables outside of their declaration scope
And run all tests locally before pushing changes.
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Fixed in I addressed the CS9080 failure by making the Unix callback marshaller local Local validation run before pushing:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs:222
- The newly added
[CLSCompliant(false)]attribute is applied toProcess.Start(string fileName), but the implementation method is not marked CLS-noncompliant and the signature is CLS-compliant. This creates ref/runtime metadata mismatch and changes the public contract for an existing API.
[System.CLSCompliantAttribute(false)]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
[System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] // this needs to come after the ios attribute due to limitations in the platform analyzer
public static System.Diagnostics.Process Start(string fileName) { throw null; }
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
…form attrs, cwd normalization Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
| /// A function that receives the prepared <see cref="UnixProcessStartArguments"/> and creates the process using any system call of the user's choice. | ||
| /// The callback must return a valid <see cref="SafeProcessHandle"/> for the newly created process. | ||
| /// The memory referenced by pointer properties in <see cref="UnixProcessStartArguments"/> is only valid for the duration of the callback. | ||
| /// The callback is invoked while an internal process-start lock is held; calling APIs that start processes from within the callback may deadlock or throw. |
There was a problem hiding this comment.
| /// The callback is invoked while an internal process-start lock is held; calling APIs that start processes from within the callback may deadlock or throw. | |
| /// The callback is invoked while an internal process-start lock is held; calling System.Diagnostic.Process APIs that start processes from within the callback may deadlock or throw. |
Be specific that this applies to S.D.Process APIs only. Calling OS APIs that start new processes is fine and actually required.
(Fix this for both versions of OS process start arguments.)
There was a problem hiding this comment.
Updated in c152ec2. I narrowed the lock warning in both UnixProcessStartArguments and WindowsProcessStartArguments to explicitly call out only System.Diagnostics.Process APIs that start processes; OS process-start APIs remain valid for the callback scenario.
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
| } | ||
|
|
||
| // Get SafeProcessHandle from the pid. | ||
| // In the future, SafeProcessHandle.Open will be used instead. |
main PR
Description
Adds
WindowsProcessStartArguments.Start(ProcessStartInfo, Func<WindowsProcessStartArguments, SafeProcessHandle>)andUnixProcessStartArguments.Start(ProcessStartInfo, Func<UnixProcessStartArguments, SafeProcessHandle>)in theSystem.Runtime.InteropServicesnamespace to let callers create processes with custom platform APIs while still usingProcessinfrastructure (ReadAllText, async stream handling,WaitForExit, etc.).The two new
ref structtypes —WindowsProcessStartArgumentsandUnixProcessStartArguments— carry the prepared start data (standard handles, command line/argv, environment block, and on Unix the resolved executable path as a UTF-8 pointer) into the caller-supplied callback. Theref structconstraint ties the lifetime of pointer-typed properties to the callback invocation.This PR also incorporates review feedback across the callback path and tests, including:
cmd /c echo ... 1>&2output formatting ("error \r\n"with trailing space before\r\n)ToInt32()conversions and passing raw handles asnintconsistentlySystem.Runtime.InteropServicesnamespace[SupportedOSPlatform("windows")]added toWindowsProcessStartArgumentsimplementation and[UnsupportedOSPlatform("windows")]added toUnixProcessStartArgumentsimplementation to match the ref assemblyUnixProcessStartArguments.ResolvedPathdocs (setter isinternal)ProcessUtils.ResolveValidPathnow normalizes emptyworkingDirectorytonullfor consistent error messages[CLSCompliant(false)]removed fromProcess.Start(string)in the ref assemblyMost notably, callback executable-path data and callback argument lifetime behavior were updated based on feedback:
ProcessStartArguments.FileNamewas replaced with Unix-onlyUnixProcessStartArguments.ResolvedPathResolvedPathisbyte*and annotated with[UnsupportedOSPlatform("windows")]args.ResolvedPathdirectly forposix_spawnLatest follow-up feedback updates:
Utf8StringMarshaller.ManagedToUnmanagedInfor resolved-path UTF-8 marshallingProcessUtils.ResolveValidPathProcessUtils.ResolvePathis private and both Unix start paths useResolveValidPathCustomer Impact
Developers who need unsupported/native process creation mechanisms (for example custom
CreateProcess*/posix_spawnflows) can still rely on .NETProcessfeatures 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/tests in this repo:
./dotnet.sh build src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj -c Release -v minimal /t:Compile /p:TargetFramework=net11.0-linux./dotnet.sh test src/libraries/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj -c Release -f net11.0-unix -v minimal(Passed: 611, Failed: 0, Skipped: 5)Prerequisite setup used for local test execution:
./build.sh -subset clr+libs -rc Release -lc ReleaseRisk
Moderate. This change adds new public API surface in
System.Runtime.InteropServices(WindowsProcessStartArgumentsandUnixProcessStartArguments) and adjusts callback argument shape on Unix (ResolvedPathpointer) and callback-lifetime constraints (ref struct).Risk is mitigated by:
ResolveValidPath) to avoid divergence/duplicationUtf8StringMarshaller.ManagedToUnmanagedIn) in the callback pathToInt32()removal) and low riskPackage 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.