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();