From e5437dea2648c1ad0278a6e29c0195761ce6e5f5 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 1 Mar 2022 15:28:22 -0800 Subject: [PATCH 1/4] Add fix for process objects for processes that have exited. --- .../commands/EnterPSHostProcessCommand.cs | 62 +++++++++++++------ .../resources/RemotingErrorIdStrings.resx | 2 +- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index 756250eb8d7..10bbb58cac3 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -318,22 +318,19 @@ private static void PrepareRunspace(Runspace runspace) private Process GetProcessById(int procId) { - try - { - return Process.GetProcessById(procId); - } - catch (System.ArgumentException) + var process = PSHostProcessUtils.GetProcessById(procId); + if (process == null) { ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoProcessFoundWithId, procId)), - "EnterPSHostProcessNoProcessFoundWithId", - ErrorCategory.InvalidArgument, - this) - ); - - return null; + new ErrorRecord( + new PSArgumentException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoProcessFoundWithId, procId)), + "EnterPSHostProcessNoProcessFoundWithId", + ErrorCategory.InvalidArgument, + this) + ); } + + return process; } private Process GetProcessByHostProcessInfo(PSHostProcessInfo hostProcessInfo) @@ -403,7 +400,7 @@ private void VerifyProcess(Process process) { ThrowTerminatingError( new ErrorRecord( - new PSInvalidOperationException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoPowerShell, Process.ProcessName)), + new PSInvalidOperationException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoPowerShell, Process.Id)), "EnterPSHostProcessNoPowerShell", ErrorCategory.InvalidOperation, this) @@ -599,6 +596,12 @@ private static int[] GetProcIdsFromNames(string[] names) WildcardPattern namePattern = WildcardPattern.Get(name, WildcardOptions.IgnoreCase); foreach (var proc in processes) { + // Skip processes that have already terminated. + if (proc.HasExited) + { + continue; + } + if (namePattern.IsMatch(proc.ProcessName)) { returnIds.Add(proc.Id); @@ -681,10 +684,9 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc string pName = namedPipe.Substring(pNameIndex + 1); Process process = null; - try { - process = System.Diagnostics.Process.GetProcessById(id); + process = PSHostProcessUtils.GetProcessById(id); } catch (Exception) { @@ -796,8 +798,8 @@ internal PSHostProcessInfo( MainWindowTitle = string.Empty; try { - var proc = Process.GetProcessById(processId); - MainWindowTitle = proc.MainWindowTitle ?? string.Empty; + var process = PSHostProcessUtils.GetProcessById(processId); + MainWindowTitle = process?.MainWindowTitle ?? string.Empty; } catch (ArgumentException) { @@ -831,4 +833,28 @@ public string GetPipeNameFilePath() } #endregion + + #region + + internal static class PSHostProcessUtils + { + /// + /// Return a System.Diagnostics.Process object by process Id, + /// or null if not found or process has exited. + /// + public static Process GetProcessById(int procId) + { + try + { + var process = Process.GetProcessById(procId); + return process.HasExited ? null : process; + } + catch (System.ArgumentException) + { + return null; + } + } + } + + #endregion } diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 680e6d1a6b5..6cbcdc72ddb 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1408,7 +1408,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro Multiple processes were found with this name {0}. Use the process Id to specify a single process to enter. - Cannot enter process {0} because it has not loaded the PowerShell engine. + Cannot enter process with Id '{0}' because it has not loaded the PowerShell engine. No process was found with Id: {0}. From 569ec1eeb9790cad0647b41931091140519ab98a Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 1 Mar 2022 15:44:08 -0800 Subject: [PATCH 2/4] Fix code factor --- .../engine/remoting/commands/EnterPSHostProcessCommand.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index 10bbb58cac3..c8a5a945ea9 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -842,6 +842,8 @@ internal static class PSHostProcessUtils /// Return a System.Diagnostics.Process object by process Id, /// or null if not found or process has exited. /// + /// + /// Process object or null. public static Process GetProcessById(int procId) { try From e35f4d85624fa6db899465456e670cbb2ca07ef2 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 1 Mar 2022 15:45:51 -0800 Subject: [PATCH 3/4] Fix typo --- .../engine/remoting/commands/EnterPSHostProcessCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index c8a5a945ea9..4fdadd9a48d 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -842,7 +842,7 @@ internal static class PSHostProcessUtils /// Return a System.Diagnostics.Process object by process Id, /// or null if not found or process has exited. /// - /// + /// Process of Id to find. /// Process object or null. public static Process GetProcessById(int procId) { From 32733c75b0ba2097fed2a707acba6dcb37bcdf8e Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Wed, 2 Mar 2022 09:30:08 -0800 Subject: [PATCH 4/4] Add try/catch for possible race condition on exiting process. --- .../commands/EnterPSHostProcessCommand.cs | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index 4fdadd9a48d..c3ad65b0506 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -319,7 +319,7 @@ private static void PrepareRunspace(Runspace runspace) private Process GetProcessById(int procId) { var process = PSHostProcessUtils.GetProcessById(procId); - if (process == null) + if (process is null) { ThrowTerminatingError( new ErrorRecord( @@ -602,9 +602,16 @@ private static int[] GetProcIdsFromNames(string[] names) continue; } - if (namePattern.IsMatch(proc.ProcessName)) + try { - returnIds.Add(proc.Id); + if (namePattern.IsMatch(proc.ProcessName)) + { + returnIds.Add(proc.Id); + } + } + catch (InvalidOperationException) + { + // Ignore if process has exited in the mean time. } } } @@ -706,10 +713,20 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc // best effort to cleanup } } - else if (process.ProcessName.Equals(pName, StringComparison.Ordinal)) + else { - // only add if the process name matches - procAppDomainInfo.Add(new PSHostProcessInfo(pName, id, appDomainName, namedPipe)); + try + { + if (process.ProcessName.Equals(pName, StringComparison.Ordinal)) + { + // only add if the process name matches + procAppDomainInfo.Add(new PSHostProcessInfo(pName, id, appDomainName, namedPipe)); + } + } + catch (InvalidOperationException) + { + // Ignore if process has exited in the mean time. + } } } } @@ -834,7 +851,7 @@ public string GetPipeNameFilePath() #endregion - #region + #region PSHostProcessUtils internal static class PSHostProcessUtils {