Skip to content

Commit 7485838

Browse files
Copilotjkotas
andcommitted
Add non-throwing TryGetStartTime and TryGetParentProcessId helpers to avoid first-chance exceptions during enumeration
Co-authored-by: jkotas <[email protected]>
1 parent 1aac39f commit 7485838

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,65 @@ private bool WaitForInputIdleCore(int milliseconds)
328328
/// </remarks>
329329
private bool IsParentOf(Process possibleChild)
330330
{
331-
try
331+
// Use non-throwing helpers to avoid first-chance exceptions during enumeration.
332+
// This is critical for performance when a debugger is attached.
333+
if (!TryGetStartTime(out DateTime myStartTime) ||
334+
!possibleChild.TryGetStartTime(out DateTime childStartTime) ||
335+
!possibleChild.TryGetParentProcessId(out int childParentId))
332336
{
333-
return StartTime < possibleChild.StartTime && Id == possibleChild.ParentProcessId;
337+
return false;
334338
}
335-
catch (Exception e) when (IsProcessInvalidException(e))
339+
340+
return myStartTime < childStartTime && Id == childParentId;
341+
}
342+
343+
/// <summary>
344+
/// Try to get the process start time without throwing exceptions.
345+
/// </summary>
346+
private bool TryGetStartTime(out DateTime startTime)
347+
{
348+
startTime = default;
349+
using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, false))
336350
{
337-
return false;
351+
if (handle.IsInvalid)
352+
{
353+
return false;
354+
}
355+
356+
ProcessThreadTimes processTimes = new ProcessThreadTimes();
357+
if (!Interop.Kernel32.GetProcessTimes(handle,
358+
out processTimes._create, out processTimes._exit,
359+
out processTimes._kernel, out processTimes._user))
360+
{
361+
return false;
362+
}
363+
364+
startTime = processTimes.StartTime;
365+
return true;
366+
}
367+
}
368+
369+
/// <summary>
370+
/// Try to get the parent process ID without throwing exceptions.
371+
/// </summary>
372+
private unsafe bool TryGetParentProcessId(out int parentProcessId)
373+
{
374+
parentProcessId = 0;
375+
using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, false))
376+
{
377+
if (handle.IsInvalid)
378+
{
379+
return false;
380+
}
381+
382+
Interop.NtDll.PROCESS_BASIC_INFORMATION info;
383+
if (Interop.NtDll.NtQueryInformationProcess(handle, Interop.NtDll.ProcessBasicInformation, &info, (uint)sizeof(Interop.NtDll.PROCESS_BASIC_INFORMATION), out _) != 0)
384+
{
385+
return false;
386+
}
387+
388+
parentProcessId = (int)info.InheritedFromUniqueProcessId;
389+
return true;
338390
}
339391
}
340392

@@ -359,14 +411,14 @@ private unsafe int ParentProcessId
359411

360412
private bool Equals(Process process)
361413
{
362-
try
363-
{
364-
return Id == process.Id && StartTime == process.StartTime;
365-
}
366-
catch (Exception e) when (IsProcessInvalidException(e))
414+
// Use non-throwing helper to avoid first-chance exceptions during enumeration.
415+
if (!TryGetStartTime(out DateTime myStartTime) ||
416+
!process.TryGetStartTime(out DateTime otherStartTime))
367417
{
368418
return false;
369419
}
420+
421+
return Id == process.Id && myStartTime == otherStartTime;
370422
}
371423

372424
private List<Exception>? KillTree()

0 commit comments

Comments
 (0)