6

Using .Net Standard, is there a cross-platform way to get a process's parent process ID?

Several SO questions and answers address how to do this in Windows-specific ways (e.g. How to get parent process in .NET in managed way), typically using either WMI's Win32_Process, PInvoke or PerformanceCounters. However, none of these work in the cross-platform world of .Net Standard.

Update

It doesn't seem like there's currently a way to do this. I've created a couple GitHub issues suggesting that related functionality be added to .Net Standard.

Community
  • 1
  • 1
Ben Gribaudo
  • 5,057
  • 1
  • 40
  • 75

2 Answers2

0

Summarizing the discussions at github, there's a point: there should be no way in netCore to get the parent process id.

I'm sad.

homk
  • 268
  • 2
  • 12
0
public static int GetParentId(this Process process)
{
    PropertyInfo? parentPropertyInfo;

    try
    {
        // Property is available on most platforms, including Windows and Linux, but not on .NET Framework
        parentPropertyInfo = typeof(Process).GetProperty("ParentProcessId", BindingFlags.Instance | BindingFlags.NonPublic);
    }
    catch (AmbiguousMatchException)
    {
        parentPropertyInfo = null;
    }

    if (parentPropertyInfo != null)
    {
        try
        {
            return (int)parentPropertyInfo.GetValue(process);
        }
        catch (TargetInvocationException ex)
        {
            throw ex.InnerException ?? ex;
        }
    }

    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
    {
        // Works on any Windows platform independent from .NET version
        return ProcessBasicInformation.GetParentProcessId(process);
    }

    throw new NotSupportedException("Your platform does not support querying the parent process id");
}

public class ProcessBasicInformation
{
    // Class for native method: Do NOT add or remove fields. Do NOT change the order of the fields
    private readonly IntPtr exitStatus;
    private readonly IntPtr processEnvironmentBlockBaseAddress;
    private readonly IntPtr affinityMask;
    private readonly IntPtr basePriority;
    private readonly IntPtr processId;
    private readonly IntPtr parentProcessId;

    public static int GetParentProcessId(Process process)
    {
        return FillFields(process).parentProcessId.ToInt32();
    }

    private static ProcessBasicInformation FillFields(Process process)
    {
        var processBasicInformation = new ProcessBasicInformation();
        NativeMethods.NtQueryInformationProcess(process, processBasicInformation);
        return processBasicInformation;
    }
}

public class NativeMethods
{
    public static void NtQueryInformationProcess(Process process, ProcessBasicInformation processInformation)
    {
        if (Environment.OSVersion.Platform != PlatformID.Win32NT)
        {
            throw new NotSupportedException($"{nameof(NtQueryInformationProcess)} only works on Windows");
        }

        var nativeStructureSize = Marshal.SizeOf<ProcessBasicInformation>();

        var status = TryNtQueryInformationProcess(process.Handle, 0, processInformation, nativeStructureSize, out var returnLength);

        if (status != 0)
        {
            throw new Win32Exception(status);
        }

        if (returnLength != nativeStructureSize)
        {
            throw new NotSupportedException("Your Windows version does not support getting the parent process id");
        }
    }

    [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
    [DllImport("ntdll.dll", EntryPoint = "NtQueryInformationProcess")]
    private static extern int TryNtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ProcessBasicInformation processInformation, int processInformationLength, out int returnLength);