Add Process.TryGetProcessById and SafeProcessHandle.TryOpen APIs#128695
Conversation
Implement SafeProcessHandle.TryOpen that attempts to open a process by ID without throwing when the process doesn't exist (returns false instead). Throws Win32Exception only for permission errors. Implement Process.TryGetProcessById that delegates to SafeProcessHandle.TryOpen. Refactor SafeProcessHandle.Open to delegate to TryOpenCore internally. Add ProcessOpenTests.cs with tests for the new APIs and moved tests for Open/GetProcessById from SafeProcessHandleTests.cs and ProcessTests.cs. Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…names Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
…explicitly, add Process(SafeProcessHandle) ctor, consolidate tests with Theory Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback, build the product and run all process tests locally. Don't push any changes unless all process tests are passing
…ess ctor Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…terns, remove redundant test Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
All feedback addressed. Built product and ran all process tests locally — 631 passed, 0 failed, 7 skipped (platform-specific). |
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot we are almost there, please address my feedback
|
|
||
| if (tryGet) | ||
| { | ||
| bool result = Process.TryGetProcessById(processId, out Process? found); |
There was a problem hiding this comment.
I would expect it to behave similar to Open_ExitedProcess_BehaviorDependsOnPlatform namely being able to open a process after it was killed/it exited on Windows.
Edit: I've run the test locally and confirmed that it fails on Windows:
System.Diagnostics.Tests.ProcessOpenTests.GetProcessById_KilledProcess_Fails(tryGet: True) [FAIL]
Assert.False() Failure
Expected: False
Actual: True
Stack Trace:
D:\projects\runtime_copilot\src\libraries\System.Diagnostics.Process\tests\ProcessOpenTests.cs(234,0): at System.Diagnostics.Tests.ProcessOpenTests.GetProcessById_KilledProcess_Fails(Boolean tryGet)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
D:\projects\runtime_copilot\src\libraries\System.Private.CoreLib\src\System\Reflection\MethodBaseInvoker.cs(174,0): at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
@copilot please merge GetProcessById_KilledProcess_Fails into Open_ExitedProcess_BehaviorDependsOnPlatform, add another boolean argument that determines whether the process should exit or be killed and then just use these asserts to verify that exit/killed produces the same output in terms of Open/TryOpen
There was a problem hiding this comment.
Done — merged GetProcessById_KilledProcess_Fails into Open_ExitedProcess_BehaviorDependsOnPlatform with [InlineData(bool tryOpen, bool kill)] (4 combinations). Both exit and kill scenarios now use the same platform-dependent assertions.
…viorDependsOnPlatform with kill parameter Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
adamsitnik
left a comment
There was a problem hiding this comment.
LGTM now!
It's also passing for me locally on Windows:
----- start Thu 05/28/2026 22:05:31.74 =============== To repro directly: =====================================================
pushd D:\projects\runtime_copilot\artifacts\bin\System.Diagnostics.Process.Tests\Debug\net11.0-windows\
"D:\projects\runtime_copilot\artifacts\bin\testhost\net11.0-windows-Debug-x64\dotnet.exe" exec --runtimeconfig System.Diagnostics.Process.Tests.runtimeconfig.json --depsfile System.Diagnostics.Process.Tests.deps.json C:\Users\adsitnik\.nuget\packages\microsoft.dotnet.xunitconsolerunner\2.9.3-beta.26257.113\build\..\tools\net\xunit.console.dll System.Diagnostics.Process.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
popd
===========================================================================================================
Discovering: System.Diagnostics.Process.Tests (method display = ClassAndMethod, method display options = None)
Discovered: System.Diagnostics.Process.Tests (found 409 of 436 test cases)
Starting: System.Diagnostics.Process.Tests (parallel test collections = on [24 threads], stop on fail = off)
System.Diagnostics.Tests.ProcessStartInfoTests.TestWindowStyle [SKIP]
Condition(s) not met: "IsAdmin_IsNotNano_RemoteExecutorIsSupported"
System.Diagnostics.Tests.ProcessStartInfoTests.ShellExecute_Nano_Fails_Start [SKIP]
Condition(s) not met: "IsWindowsNanoServer"
0 File(s) copied
Invalid number of parameters
System.Diagnostics.Tests.ProcessOpenTests.TryOpen_ProcessOwnedByRoot_ThrowsUnauthorizedAccessException [SKIP]
Condition(s) not met: "IsLinux"
System.Diagnostics.Tests.ProcessOpenTests.Open_ProcessOwnedByRoot_ThrowsUnauthorizedAccessException [SKIP]
Condition(s) not met: "IsLinux"
Finished: System.Diagnostics.Process.Tests
=== TEST EXECUTION SUMMARY ===
System.Diagnostics.Process.Tests Total: 628, Errors: 0, Failed: 0, Skipped: 4, Time: 81.868s
----- end Thu 05/28/2026 22:06:54.89 ----- exit code 0 ----------------------------------------------------------
adamsitnik
left a comment
There was a problem hiding this comment.
Overall it LGTM, we just need to remove the two tests that keep failing on Linux.
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
d039b6e
into
copilot/add-safeproceshandle-open-api
Implements the approved API from #101582 — non-throwing alternatives to
Process.GetProcessByIdandSafeProcessHandle.Openthat returnfalsewhen the target process doesn't exist, avoiding the inherent race condition.Changes
ref/System.Diagnostics.Process.cs): Added both new APIs with platform attributes and[NotNullWhen(true)]SafeProcessHandle.Windows.cs): ExtractedTryOpenCore— returnsfalseon failure, throwsUnauthorizedAccessExceptionforERROR_ACCESS_DENIEDSafeProcessHandle.Unix.cs): Same pattern — returnsfalseonkill(pid, 0)failure, throwsUnauthorizedAccessExceptiononEPERMSafeProcessHandle.cs: Added publicTryOpenwith argument validation + PNSE guard; refactoredOpento delegate directly toTryOpen; XML docs updated to referenceUnauthorizedAccessExceptionProcess.cs: Addedprivate Process(SafeProcessHandle processHandle)ctor to avoid re-acquiring the handle;TryGetProcessByIddelegates toSafeProcessHandle.TryOpenand passes the handle to the new ctorProcessOpenTests.cs): ConsolidatedOpen/TryOpenandGetProcessById/TryGetProcessByIdtests using[ConditionalTheory]with[InlineData]; added OS-specificUnauthorizedAccessExceptiontests (Windows: lsass, Linux: PID 1) guarded byIsNotPrivilegedProcess; tests verify Kill + TryWaitForExit on opened handles;Open_ExitedProcess_BehaviorDependsOnPlatformuses(bool tryOpen, bool kill)parameters to verify both exit and kill produce the same platform-dependent behavior; replaced allCreateDefaultProcessusage withCreateProcess+ explicitKill()infinallyblocksSafeProcessHandleTests.cs: Fixed build errors by cleanly removing only theOpen_*tests that were moved toProcessOpenTests.cs