.Net Standard does not seem to provide a means to identify the parent or child processes of a given process.
In the Windows world, this information can be obtained or extrapolated using WMI's Win32_Process management object, performance counters or P/Invoke.
However, there doesn't seem to be a cross-platform .Net Standard way to do this. (If there is, please let me know! :-))
The ability to determine which processes are children (or grandchildren or great-grandchildren, etc.) of a process is needed in order to be able to kill a process tree without using platform-specific code.
For example, Microsoft.DotNet.Tools.Test.Utilities.ProcessExtension relies on different code paths to handle killing a process tree on Windows and Linux. Both code paths involve invoking one or more command-line processes. If this proposal were implemented, this logic could potentially be replaced with OS-agnostic .Net Standard code that iterates through the process's children, calls Kill on each of them, then calls Kill on the parent. No multiple code paths. No need to invoke command-line processes.
For completeness, it would be nice to have the ability to navigate both up (parent process) and down (child processes) the process tree.
public partial class Process : .... {
public int GetParentProcessId() { ... } // returns parent process Id currently associated with this process
public Process GetParentProcess() { … } // returns parent process currently associated with this process
public Process[] GetChildProcesses(bool includeAllDescendants = false) { ... } // setting the parameter to true results in grandchildren, great-grandchildren, etc., being included in the results; otherwise, only direct children are returned
}
At first glance, it might seem more appropriate to make the first two methods properties (e.g. public int ParentProcessId { get { ... }}). However, a process's parent can change. The API design guidelines suggest preferring a method over a property when "calling the member twice in succession results in different results."
If implementing this for Windows note that processes expose their parent PID but this can represent a parent that has terminated and the PID may have been reused. The correct way (according to Win32 folks) is to also check the start time of the parent process is not after that of the child. See https://github.com/Microsoft/msbuild/blob/b499c93e95f440b98967b8d5edd910ee8556f504/src/Shared/NativeMethodsShared.cs#L1146
If a common use case would be to terminate process trees, then exposing GetParentProcessId() might lead callers to the "pit of failure" since they will not necessarily know to check the start time.
It also suggests that perhaps we should have an API to terminate a process tree "correctly"
If a common use case would be to terminate process trees, then exposing GetParentProcessId() might lead callers to the "pit of failure" since they will not necessarily know to check the start time.
@danmosemsft , good point! For my purposes, I wouldn't be opposed to dropping GetParentProcessId(). The nice thing about GetParentProcess() and GetChildProcesses() is that they can factor in the start time check.
It also suggests that perhaps we should have an API to terminate a process tree "correctly"
I like the idea. Maybe something like:
Kill(bool entireProcessTree = false); // false = existing behavior of Kill()
Can you think of use cases that would make it desirable to also offer an option to recursively CloseMainWindow() on the entire process tree?
It seems to me that GetParentProcess() and GetChildProcesses() could lead to the pit of failure as well, since the Process object when initialized is keyed by the process ID, which can be repurposed. It does not get a handle to the process until it needs one. So GetParentProcess() eg could give you a Process object that when you ultimately use it could act on an unrelated process.
If the scenario is killing a tree, can we just add Kill(bool includeChildren = false) only?
I haven't personally had a scenario for CloseMainWindow() recursively. That can be added later if a scenario emerges.
If this makes sense please update the top comment (and title) as that's what the API review looks at.
Thanks, @danmosemsft! I created a new issue (#26234) for the kill all proposal. I left this as a separate issue as an alternative/complement to the new issue. If this issue doesn't gain traction soon (it's fine with me if it doesn't), I should probably close it.
I ran into a need for this, too. If choosing between this and https://github.com/dotnet/corefx/issues/26234, I would prefer dotnet/corefx#26234.
Either way, I think this is a good API. It appears there is a real need for working with process trees. Process tree killing has been implemented in several places already -- in MSBuild, ASP.NET Core, and the CLI. Would be nice to have one, common version of it in corefx.
@joperezr perhaps you could guide this and/or https://github.com/dotnet/corefx/issues/26234 to api-ready-for-review if appropriate?
Hi @joperezr (or @danmosemsft)! Is there anything I could do to help further this along?
Should
```c#
public Process[] GetChildProcesses(bool includeAllDescendants = false)
```c#
public IEnumerable<Process> GetChildProcesses(bool includeAllDescendants = false)
?
It seems the real scenario is killing a process tree. We're generally hesitant to add policy with complex policy where the behavior is hard to predict (although I personally believe killing the process is reasonably well defined as "kill as much as possible and keep going").
The trouble with exposing process navigation APIs that walking up often fails due to security concerns (walking down usually seem to succeed though).
My concern is that if the primary scenario is killing a tree, I'd really prefer this to be a single method call as this has a much higher chance of being reliable and a custom walk with exception handling.
@JeremyKuhne, do you own process?
@wtgodbe, @krwq own Process (see owners list)
I suggest adding Process.Kill(includeDescendants = false) (not sure whether it should return void or not, but for safety descendants should be excluded by default) but _not_ adding parent/child accessors because there is no other scenario at the moment and they are likely to have subtle behaviors.
Process.Kill(includeDescendants = false) (or the equivalent) meets my needs. The idea of adding child & parent navigation (this issue) was an attempt to come up with a more generic way to provide what I needed to manually do the kill all descendants.
So, maybe move forward with dotnet/corefx#26234 and let this issue wait until someone presents a use scenario that specifically needs what it suggests?
@bgribaudo sounds good, could you please update your top post with the reduced proposal? API review looks at that - should be able to review againnext week
@danmosemsft, thanks! Would you like me to do that on this issue or over on dotnet/corefx#26234?
@bgribaudo oh, good point. Let's leave this aside then. I'll mark as api-needs-work while we wait for scenarios. Or you can close if you want.
Thanks, @danmosemsft!
Stumbled across this while looking for a an API to get parent process.
An application we're porting to .NET Core changes behaviour based on what parent process launched it. We were using p/invoke on ntdll.dll but that is obviously not cross-platform. Our current choice is to runtime test around the call and to choose a good default behaviour for other platforms while emitting a warning.
Most helpful comment
Process.Kill(includeDescendants = false)(or the equivalent) meets my needs. The idea of adding child & parent navigation (this issue) was an attempt to come up with a more generic way to provide what I needed to manually do the kill all descendants.So, maybe move forward with dotnet/corefx#26234 and let this issue wait until someone presents a use scenario that specifically needs what it suggests?