diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 4a611d72ab109e..340acef5f07fd3 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -434,14 +434,5 @@ private SafeWaitHandle GetSafeWaitHandle() private static bool WaitForInputIdleCore(int _ /*milliseconds*/) => throw new InvalidOperationException(SR.InputIdleUnknownError); - /// Gets the friendly name of the process. - public string ProcessName - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.ProcessName; - } - } } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index ab41ea67e53872..18a91393b61d42 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -16,8 +16,6 @@ namespace System.Diagnostics { public partial class Process : IDisposable { - private string? _processName; - private bool _haveMainWindow; private IntPtr _mainWindowHandle; private string? _mainWindowTitle; @@ -113,7 +111,6 @@ private void RefreshCore() _haveMainWindow = false; _mainWindowTitle = null; _haveResponding = false; - _processName = null; } /// Additional logic invoked when the Process is closed. @@ -534,39 +531,6 @@ private SafeProcessHandle GetProcessHandle(int access, bool throwIfExited = true private static ConsoleEncoding GetStandardOutputEncoding() => GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP()); - /// Gets the friendly name of the process. - public string ProcessName - { - get - { - if (_processName == null) - { - // If we already have the name via a populated ProcessInfo - // then use that one. - if (_processInfo?.ProcessName != null) - { - _processName = _processInfo!.ProcessName; - } - else - { - // Ensure that the process is not yet exited - EnsureState(State.HaveNonExitedId); - _processName = ProcessManager.GetProcessName(_processId, _machineName); - - // Fallback to slower ProcessInfo implementation if optimized way did not return a - // process name (e.g. in case of missing permissions for Non-Admin users) - if (_processName == null) - { - EnsureState(State.HaveProcessInfo); - _processName = _processInfo!.ProcessName; - } - } - } - - return _processName; - } - } - private bool StartCore(ProcessStartInfo startInfo, SafeFileHandle? stdinHandle, SafeFileHandle? stdoutHandle, SafeFileHandle? stderrHandle) { SafeProcessHandle startedProcess = SafeProcessHandle.StartCore(startInfo, stdinHandle, stdoutHandle, stderrHandle); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index 47db74fc6ee6b2..e90b3f93dce0cc 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.Serialization; @@ -31,6 +32,7 @@ public partial class Process : Component private bool _isRemoteMachine; private string _machineName; private ProcessInfo? _processInfo; + private string? _processName; private ProcessThreadCollection? _threads; private ProcessModuleCollection? _modules; @@ -143,13 +145,7 @@ private bool Associated /// /// public int BasePriority - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.BasePriority; - } - } + => GetProcessInfo().BasePriority; /// /// @@ -255,6 +251,21 @@ public string MachineName } } + /// Gets the friendly name of the process. + public string ProcessName + { + get + { + EnsureState(State.HaveNonExitedId); + string? processName = _processName ??= ProcessManager.GetProcessName(_processId, _machineName, ref _processInfo); + if (processName is null) + { + ThrowNoProcessInfo(); + } + return processName; + } + } + /// /// Gets or sets the maximum allowable working set for the associated process. /// @@ -317,121 +328,49 @@ public ProcessModuleCollection Modules } public long NonpagedSystemMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.PoolNonPagedBytes; - } - } + => GetProcessInfo().PoolNonPagedBytes; [Obsolete("Process.NonpagedSystemMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.NonpagedSystemMemorySize64 instead.")] public int NonpagedSystemMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.PoolNonPagedBytes); - } - } + => unchecked((int)GetProcessInfo().PoolNonPagedBytes); public long PagedMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.PageFileBytes; - } - } + => GetProcessInfo().PageFileBytes; [Obsolete("Process.PagedMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PagedMemorySize64 instead.")] public int PagedMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.PageFileBytes); - } - } + => unchecked((int)GetProcessInfo().PageFileBytes); public long PagedSystemMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.PoolPagedBytes; - } - } + => GetProcessInfo().PoolPagedBytes; [Obsolete("Process.PagedSystemMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PagedSystemMemorySize64 instead.")] public int PagedSystemMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.PoolPagedBytes); - } - } + => unchecked((int)GetProcessInfo().PoolPagedBytes); public long PeakPagedMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.PageFileBytesPeak; - } - } + => GetProcessInfo().PageFileBytesPeak; [Obsolete("Process.PeakPagedMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PeakPagedMemorySize64 instead.")] public int PeakPagedMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.PageFileBytesPeak); - } - } + => unchecked((int)GetProcessInfo().PageFileBytesPeak); public long PeakWorkingSet64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.WorkingSetPeak; - } - } + => GetProcessInfo().WorkingSetPeak; [Obsolete("Process.PeakWorkingSet has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PeakWorkingSet64 instead.")] public int PeakWorkingSet - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.WorkingSetPeak); - } - } + => unchecked((int)GetProcessInfo().WorkingSetPeak); public long PeakVirtualMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.VirtualBytesPeak; - } - } + => GetProcessInfo().VirtualBytesPeak; [Obsolete("Process.PeakVirtualMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PeakVirtualMemorySize64 instead.")] public int PeakVirtualMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.VirtualBytesPeak); - } - } + => unchecked((int)GetProcessInfo().VirtualBytesPeak); /// /// @@ -490,23 +429,11 @@ public ProcessPriorityClass PriorityClass } public long PrivateMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.PrivateBytes; - } - } + => GetProcessInfo().PrivateBytes; [Obsolete("Process.PrivateMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.PrivateMemorySize64 instead.")] public int PrivateMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.PrivateBytes); - } - } + => unchecked((int)GetProcessInfo().PrivateBytes); /// /// @@ -536,13 +463,7 @@ public IntPtr ProcessorAffinity } public int SessionId - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.SessionId; - } - } + => GetProcessInfo().SessionId; /// /// @@ -589,12 +510,12 @@ public ProcessThreadCollection Threads { if (_threads == null) { - EnsureState(State.HaveProcessInfo); - int count = _processInfo!._threadInfoList.Count; + ProcessInfo processInfo = GetProcessInfo(); + int count = processInfo._threadInfoList.Count; ProcessThread[] newThreadsArray = new ProcessThread[count]; for (int i = 0; i < count; i++) { - newThreadsArray[i] = new ProcessThread(_isRemoteMachine, _processId, (ThreadInfo)_processInfo._threadInfoList[i]); + newThreadsArray[i] = new ProcessThread(_isRemoteMachine, _processId, (ThreadInfo)processInfo._threadInfoList[i]); } ProcessThreadCollection newThreads = new ProcessThreadCollection(newThreadsArray); @@ -608,32 +529,20 @@ public int HandleCount { get { - EnsureState(State.HaveProcessInfo); + ProcessInfo processInfo = GetProcessInfo(); EnsureHandleCountPopulated(); - return _processInfo!.HandleCount; + return processInfo.HandleCount; } } partial void EnsureHandleCountPopulated(); public long VirtualMemorySize64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.VirtualBytes; - } - } + => GetProcessInfo().VirtualBytes; [Obsolete("Process.VirtualMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.VirtualMemorySize64 instead.")] public int VirtualMemorySize - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.VirtualBytes); - } - } + => unchecked((int)GetProcessInfo().VirtualBytes); /// /// @@ -740,23 +649,11 @@ public StreamReader StandardError } public long WorkingSet64 - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.WorkingSet; - } - } + => GetProcessInfo().WorkingSet; [Obsolete("Process.WorkingSet has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.WorkingSet64 instead.")] public int WorkingSet - { - get - { - EnsureState(State.HaveProcessInfo); - return unchecked((int)_processInfo!.WorkingSet); - } - } + => unchecked((int)GetProcessInfo().WorkingSet); public event EventHandler Exited { @@ -971,22 +868,6 @@ private void EnsureState(State state) throw new NotSupportedException(SR.NotSupportedRemote); } - if ((state & State.HaveProcessInfo) != (State)0) - { - if (_processInfo == null) - { - if ((state & State.HaveNonExitedId) != State.HaveNonExitedId) - { - EnsureState(State.HaveNonExitedId); - } - _processInfo = ProcessManager.GetProcessInfo(_processId, _machineName); - if (_processInfo == null) - { - throw new InvalidOperationException(SR.NoProcessInfo); - } - } - } - if ((state & State.Exited) != (State)0) { if (!HasExited) @@ -1178,6 +1059,7 @@ public void Refresh() _havePriorityClass = false; _haveExitTime = false; _havePriorityBoostEnabled = false; + _processName = null; RefreshCore(); } @@ -1497,14 +1379,10 @@ public override string ToString() { if (Associated) { - _processInfo ??= ProcessManager.GetProcessInfo(_processId, _machineName); - if (_processInfo is not null) + string? processName = _processName ??= ProcessManager.GetProcessName(_processId, _machineName, ref _processInfo); + if (!string.IsNullOrEmpty(processName)) { - string processName = _processInfo.ProcessName; - if (processName.Length != 0) - { - result = $"{result} ({processName})"; - } + result = $"{result} ({processName})"; } } } @@ -1862,9 +1740,27 @@ private enum State HaveId = 0x1, IsLocal = 0x2, HaveNonExitedId = HaveId | 0x4, - HaveProcessInfo = 0x8, - Exited = 0x10, - Associated = 0x20, + Exited = 0x8, + Associated = 0x10, } + + private ProcessInfo GetProcessInfo() + { + ProcessInfo? processInfo = _processInfo; + if (processInfo == null) + { + EnsureState(State.HaveNonExitedId); + _processInfo = processInfo = ProcessManager.GetProcessInfo(_processId, _machineName); + if (processInfo == null) + { + ThrowNoProcessInfo(); + } + } + return processInfo; + } + + [DoesNotReturn] + private static void ThrowNoProcessInfo() => + throw new InvalidOperationException(SR.NoProcessInfo); } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs index a1b9583cebc41e..259a5ae4ca89aa 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs @@ -20,6 +20,19 @@ internal static string GetProcPath(int processId) return Interop.Process.GetProcPath(processId); } + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) + { + ThrowIfRemoteMachine(machineName); + + if (processInfo is not null) + { + return processInfo.ProcessName; + } + + processInfo = CreateProcessInfo(processId); + return processInfo?.ProcessName; + } + internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null) { // Negative PIDs aren't valid diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs index 14ead91751253a..e36725c033408f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs @@ -38,6 +38,24 @@ public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string ma return processes.ToArray(); } + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) + { + ThrowIfRemoteMachine(machineName); + + if (processInfo is not null) + { + return processInfo.ProcessName; + } + + if (TryGetProcPid(processId, out Interop.procfs.ProcPid procPid) && + Interop.procfs.TryReadStatFile(procPid, out Interop.procfs.ParsedStat stat)) + { + return Process.GetUntruncatedProcessName(procPid, ref stat); + } + + return null; + } + /// Gets an array of module infos for the specified process. /// The ID of the process whose modules should be enumerated. /// The array of modules. diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs index d09790a4230fa6..f0df4429cd0c70 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs @@ -21,7 +21,23 @@ private static string GetProcPath(int processId) return Interop.libproc.proc_pidpath(processId); } - internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null) + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) + { + ThrowIfRemoteMachine(machineName); + + if (processInfo is not null) + { + return processInfo.ProcessName; + } + // Return empty string rather than null when the process name can't be determined + // to preserve existing macOS behavior where the name defaults to "". + return GetProcessName(processId) ?? ""; + } + + internal static string? GetProcessName(int pid) + => GetProcessName(pid, out _); + + private static string? GetProcessName(int pid, out Interop.libproc.proc_taskallinfo? taskInfo, bool getInfo = false) { // Negative PIDs aren't valid ArgumentOutOfRangeException.ThrowIfNegative(pid); @@ -40,20 +56,30 @@ private static string GetProcPath(int processId) // Ignored } - // Try to get the task info. This can fail if the user permissions don't permit - // this user context to query the specified process - Interop.libproc.proc_taskallinfo? info = Interop.libproc.GetProcessInfoById(pid); + if (string.IsNullOrEmpty(processName) || getInfo) + { + // Try to get the task info. This can fail if the user permissions don't permit + // this user context to query the specified process + taskInfo = Interop.libproc.GetProcessInfoById(pid); - // If we could not get the process name from its path, attempt to use the old 15-char - // limited process name - if (string.IsNullOrEmpty(processName) && info != null) + if (taskInfo.HasValue && string.IsNullOrEmpty(processName)) + { + Interop.libproc.proc_taskallinfo temp = taskInfo.Value; + unsafe { processName = Utf8StringMarshaller.ConvertToManaged(temp.pbsd.pbi_comm); } + } + } + else { - Interop.libproc.proc_taskallinfo temp = info.Value; - unsafe { processName = Utf8StringMarshaller.ConvertToManaged(temp.pbsd.pbi_comm); } + taskInfo = default; } - // Fallback to empty string if the process name could not be retrieved in any way - processName ??= ""; + return processName; + } + + internal static ProcessInfo? CreateProcessInfo(int pid, string? processNameFilter = null) + { + Interop.libproc.proc_taskallinfo? info; + string processName = GetProcessName(pid, out info, getInfo: true) ?? ""; if (!string.IsNullOrEmpty(processNameFilter) && !string.Equals(processName, processNameFilter, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs index 9b8cc20ac57335..4c708e9d122b37 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.SunOS.cs @@ -71,6 +71,19 @@ internal static ProcessModuleCollection GetModules(int processId) return new ProcessModuleCollection(0); } + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) + { + ThrowIfRemoteMachine(machineName); + + if (processInfo is not null) + { + return processInfo.ProcessName; + } + + processInfo = CreateProcessInfo(processId); + return processInfo?.ProcessName; + } + /// /// Creates a ProcessInfo from the specified process ID. /// diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index 8aa7d8b81a770b..0e8806b091e02e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -120,28 +120,15 @@ public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string ma return null; } - /// Gets the process name for the specified process ID on the specified machine. - /// The process ID. - /// The machine name. - /// The process name for the process if it could be found; otherwise, null. - public static string? GetProcessName(int processId, string machineName) + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) { - if (IsRemoteMachine(machineName)) + if (processInfo is not null) { - // remote case: we take the hit of looping through all results - ProcessInfo[] processInfos = NtProcessManager.GetProcessInfos(machineName, isRemoteMachine: true); - foreach (ProcessInfo processInfo in processInfos) - { - if (processInfo.ProcessId == processId) - { - return processInfo.ProcessName; - } - } + return processInfo.ProcessName; } - else - { - // local case: do not use performance counter and also attempt to get the matching (by pid) process only + if (!IsRemoteMachine(machineName)) + { string? processName = Interop.Kernel32.GetProcessName((uint)processId); if (processName is not null) { @@ -152,10 +139,10 @@ public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string ma } } - return null; + processInfo = GetProcessInfo(processId, machineName); + return processInfo?.ProcessName; } - /// Gets the IDs of all processes on the specified machine. /// The machine to examine. /// An array of process IDs from the specified machine. diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.iOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.iOS.cs index 766c2dae22bd7c..be5805bb3d09c8 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.iOS.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.iOS.cs @@ -33,6 +33,11 @@ internal static ProcessModuleCollection GetModules(int processId) return new ProcessModuleCollection(0); } + internal static string? GetProcessName(int processId, string machineName, ref ProcessInfo? processInfo) + { + throw new PlatformNotSupportedException(); + } + private static ProcessInfo CreateProcessInfo(int pid) { throw new PlatformNotSupportedException();