@@ -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