如何以托管方式在.NET中获取父进程


85

我一直在寻找在.NET中获取父进程的方法,但只找到了P / Invoke方式。


5
当您的流程的多个实例都具有相同的ProcessName时,它们在运行时会发生什么?
Michael Burr

1
万一它对别人有帮助:我个人只需要父进程ID。如果父进程退出,则Michael Hale和Simon Mourier的以下解决方案将不起作用,因为它们正在Process.GetProcessById()使用ID(现在)不存在的ID进行调用。但是到那时,您具有父进程的进程ID,因此可以像我一样使用它。
泰勒·科利尔


您如何将父进程ID作为命令行参数发送?:)
John Demetriou

Answers:


62

这段代码提供了一个不错的接口来查找父流程对象,并考虑了多个具有相同名称的流程的可能性:

用法:

Console.WriteLine("ParentPid: " + Process.GetProcessById(6972).Parent().Id);

码:

public static class ProcessExtensions {
    private static string FindIndexedProcessName(int pid) {
        var processName = Process.GetProcessById(pid).ProcessName;
        var processesByName = Process.GetProcessesByName(processName);
        string processIndexdName = null;

        for (var index = 0; index < processesByName.Length; index++) {
            processIndexdName = index == 0 ? processName : processName + "#" + index;
            var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
            if ((int) processId.NextValue() == pid) {
                return processIndexdName;
            }
        }

        return processIndexdName;
    }

    private static Process FindPidFromIndexedProcessName(string indexedProcessName) {
        var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
        return Process.GetProcessById((int) parentId.NextValue());
    }

    public static Process Parent(this Process process) {
        return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
    }
}

2
方法在哪里float.As定义?
Mark Byers 2010年

22
这些是一些令人吃惊的名称不正确的方法。
标记

4
在我的测试中,这比Simon Mourier的解决方案要慢得多。而且,不幸的是,它进行了某种“将流程带入前端”机制。我不知道为什么。其他人有没有经历过?为此,我正在运行的测试是由Visual Studio创建的安装程序引导程序EXE,它将启动MSIEXEC.exe Windows安装程序。
Tyler Collier

6
不幸的是,当性能计数器类别名称已本地化时(例如,在非英语Windows上),它不起作用。
LukeSw 2012年

5
除非有迫切的理由,否则我建议使用Simon的版本,因为性能差异很大。
David Burton 2012年

150

这是一个解决方案。它使用p / invoke,但似乎可以很好地使用32或64 cpu:

    /// <summary>
    /// A utility class to determine a process parent.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ParentProcessUtilities
    {
        // These members must match PROCESS_BASIC_INFORMATION
        internal IntPtr Reserved1;
        internal IntPtr PebBaseAddress;
        internal IntPtr Reserved2_0;
        internal IntPtr Reserved2_1;
        internal IntPtr UniqueProcessId;
        internal IntPtr InheritedFromUniqueProcessId;

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);

        /// <summary>
        /// Gets the parent process of the current process.
        /// </summary>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess()
        {
            return GetParentProcess(Process.GetCurrentProcess().Handle);
        }

        /// <summary>
        /// Gets the parent process of specified process.
        /// </summary>
        /// <param name="id">The process id.</param>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess(int id)
        {
            Process process = Process.GetProcessById(id);
            return GetParentProcess(process.Handle);
        }

        /// <summary>
        /// Gets the parent process of a specified process.
        /// </summary>
        /// <param name="handle">The process handle.</param>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess(IntPtr handle)
        {
            ParentProcessUtilities pbi = new ParentProcessUtilities();
            int returnLength;
            int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
            if (status != 0)
                throw new Win32Exception(status);

            try
            {
                return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
            }
            catch (ArgumentException)
            {
                // not found
                return null;
            }
        }
    }

13
它实际上是受管的,但不能移植到您所要使用的Windows之外的其他操作系统上。但是,父进程的概念也不是可移植的,因为它不在.NET Framework本身中,所以我认为这不是一个大问题。
西蒙·穆里耶

11
大!没有慢速的性能计数器。我真的很讨厌“不受管理”的评论。与P / Invoke相比,如何更有效地查询性能计数器。
Jabe

5
不幸的是,此功能仅是内部的。MSDN“[NtQueryInformationProcess可以更改或在未来的Windows版本中不可用的应用程序应该使用本主题中列出的可选功能。]”说这 msdn.microsoft.com/en-us/library/windows/desktop/...
贾斯汀。 m.chase 2012年

21
@ justin.m.chase-它已经存在了将近20年,所以我怀疑它明天是否会被删除,并且没有可以使我知道父进程的可修改的NT函数,但是可以,使用时后果自负。
西蒙·穆里耶

4
这种方法是至少快10倍时,我比较了此方法的其它方法的性能。接受的答案打勾:2600657。这个答案蜱:8454
Mojtaba Rezaeian '16

9

这条路:

public static Process GetParent(this Process process)
{
  try
  {
    using (var query = new ManagementObjectSearcher(
      "SELECT * " +
      "FROM Win32_Process " +
      "WHERE ProcessId=" + process.Id))
    {
      return query
        .Get()
        .OfType<ManagementObject>()
        .Select(p => Process.GetProcessById((int)(uint)p["ParentProcessId"]))
        .FirstOrDefault();
    }
  }
  catch
  {
    return null;
  }
}

2
可以,但是WMI可能很慢(秒).pinvoke是行之有效的方法。
Alastair花胶

4

这是我对托管解决方案的尝试。

它轮询所有进程的性能计数器,并将子PID的字典返回给父PID。然后,您可以使用当前的PID检查字典,以查看您的父母,祖父母等。

可以肯定的是,获取多少信息是过分的。随时进行优化。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace PidExamples
{
    class ParentPid
    {
        static void Main(string[] args)
        {
            var childPidToParentPid = GetAllProcessParentPids();
            int currentProcessId = Process.GetCurrentProcess().Id;

            Console.WriteLine("Current Process ID: " + currentProcessId);
            Console.WriteLine("Parent Process ID: " + childPidToParentPid[currentProcessId]);
        }

        public static Dictionary<int, int> GetAllProcessParentPids()
        {
            var childPidToParentPid = new Dictionary<int, int>();

            var processCounters = new SortedDictionary<string, PerformanceCounter[]>();
            var category = new PerformanceCounterCategory("Process");

            // As the base system always has more than one process running, 
            // don't special case a single instance return.
            var instanceNames = category.GetInstanceNames();
            foreach(string t in instanceNames)
            {
                try
                {
                    processCounters[t] = category.GetCounters(t);
                }
                catch (InvalidOperationException)
                {
                    // Transient processes may no longer exist between 
                    // GetInstanceNames and when the counters are queried.
                }
            }

            foreach (var kvp in processCounters)
            {
                int childPid = -1;
                int parentPid = -1;

                foreach (var counter in kvp.Value)
                {
                    if ("ID Process".CompareTo(counter.CounterName) == 0)
                    {
                        childPid = (int)(counter.NextValue());
                    }
                    else if ("Creating Process ID".CompareTo(counter.CounterName) == 0)
                    {
                        parentPid = (int)(counter.NextValue());
                    }
                }

                if (childPid != -1 && parentPid != -1)
                {
                    childPidToParentPid[childPid] = parentPid;
                }
            }

            return childPidToParentPid;
        }
    }
}    

在其他新闻中,我了解到我的机器上有多少个性能计数器:13401。


2
此方法有效,但似乎非常慢。我的机器花了10秒钟以上。
卡斯滕

3

如果接受P / Invoke,则有一个更好的方法,它比NtQueryInformationProcess有更多的文献资料:PROCESSENTRY32(CreateToolhelp32Snapshot,Process32First,Process32Next)。它显示在这篇文章中

请注意细微的细节, 并注意父PID不一定是创建者PID,实际上,正如PROCESSENTRY32上的社区评论所指出的那样,父PID可能是完全不相关的 。


2

如果您曾经研究过BCL,则应发现故意避免了寻找父进程的方法,例如:

https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/ProcessManager.cs,327

正如您在源代码中看到的那样,它包含全面的结构和导入的本机方法,这些绝对足够完成工作。但是,即使您通过反射访问它们(这是可能的),您也找不到直接执行此操作的方法。我无法回答为什么,但是这种现象会引起类似您的问题的反复询问。例如:

如何获取应用程序父进程的PID

因为在此线程中没有使用CreateToolhelp32Snapshot的代码的答案,所以我将其添加了-我从MS参考源中窃取的结构定义和名称的一部分:)

  • using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Collections.Generic;
    using System.Linq;
    using System;
    

    public static class Toolhelp32 {
        public const uint Inherit = 0x80000000;
        public const uint SnapModule32 = 0x00000010;
        public const uint SnapAll = SnapHeapList|SnapModule|SnapProcess|SnapThread;
        public const uint SnapHeapList = 0x00000001;
        public const uint SnapProcess = 0x00000002;
        public const uint SnapThread = 0x00000004;
        public const uint SnapModule = 0x00000008;
    
        [DllImport("kernel32.dll")]
        static extern bool CloseHandle(IntPtr handle);
        [DllImport("kernel32.dll")]
        static extern IntPtr CreateToolhelp32Snapshot(uint flags, int processId);
    
        public static IEnumerable<T> TakeSnapshot<T>(uint flags, int id) where T : IEntry, new() {
            using(var snap = new Snapshot(flags, id))
                for(IEntry entry = new T { }; entry.TryMoveNext(snap, out entry);)
                    yield return (T)entry;
        }
    
        public interface IEntry {
            bool TryMoveNext(Toolhelp32.Snapshot snap, out IEntry entry);
        }
    
        public struct Snapshot:IDisposable {
            void IDisposable.Dispose() {
                Toolhelp32.CloseHandle(m_handle);
            }
            public Snapshot(uint flags, int processId) {
                m_handle=Toolhelp32.CreateToolhelp32Snapshot(flags, processId);
            }
            IntPtr m_handle;
        }
    }
    

    [StructLayout(LayoutKind.Sequential)]
    public struct WinProcessEntry:Toolhelp32.IEntry {
        [DllImport("kernel32.dll")]
        public static extern bool Process32Next(Toolhelp32.Snapshot snap, ref WinProcessEntry entry);
    
        public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
            var x = new WinProcessEntry { dwSize=Marshal.SizeOf(typeof(WinProcessEntry)) };
            var b = Process32Next(snap, ref x);
            entry=x;
            return b;
        }
    
        public int dwSize;
        public int cntUsage;
        public int th32ProcessID;
        public IntPtr th32DefaultHeapID;
        public int th32ModuleID;
        public int cntThreads;
        public int th32ParentProcessID;
        public int pcPriClassBase;
        public int dwFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public String fileName;
        //byte fileName[260];
        //public const int sizeofFileName = 260;
    }
    

    public static class Extensions {
        public static Process Parent(this Process p) {
            var entries = Toolhelp32.TakeSnapshot<WinProcessEntry>(Toolhelp32.SnapAll, 0);
            var parentid = entries.First(x => x.th32ProcessID==p.Id).th32ParentProcessID;
            return Process.GetProcessById(parentid);
        }
    }
    

我们可以像这样使用它:

  • 测试

    public class TestClass {
        public static void TestMethod() {
            var p = Process.GetCurrentProcess().Parent();
            Console.WriteLine("{0}", p.Id);
        }
    }
    

对于替代结局..

根据文档,每种类型的条目都有一对迭代方法,例如Process32FirstProcess32Next用于过程的迭代。但是我发现不必要使用“ xxxxFirst”方法,然后我想为什么不将迭代方法及其相应的条目类型放入?它更容易实现和理解(我想是..)。

就像Toolhelp32带有帮助的后缀一样,我认为静态的帮助器类是合适的,因此我们可以使用明确的限定名称,例如,Toolhelp32.SnapshotToolhelp32.IEntry在这里与之无关。

一旦获得父进程,如果您进一步想要获得一些详细信息,则可以轻松地扩展它,例如,在其模块上进行迭代,然后添加:

  • 代码-WinModuleEntry

    [StructLayout(LayoutKind.Sequential)]
    public struct WinModuleEntry:Toolhelp32.IEntry { // MODULEENTRY32
        [DllImport("kernel32.dll")]
        public static extern bool Module32Next(Toolhelp32.Snapshot snap, ref WinModuleEntry entry);
    
        public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
            var x = new WinModuleEntry { dwSize=Marshal.SizeOf(typeof(WinModuleEntry)) };
            var b = Module32Next(snap, ref x);
            entry=x;
            return b;
        }
    
        public int dwSize;
        public int th32ModuleID;
        public int th32ProcessID;
        public int GlblcntUsage;
        public int ProccntUsage;
        public IntPtr modBaseAddr;
        public int modBaseSize;
        public IntPtr hModule;
        //byte moduleName[256];
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string moduleName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string fileName;
        //byte fileName[260];
        //public const int sizeofModuleName = 256;
        //public const int sizeofFileName = 260;
    }
    

    和一些测试..

    public class TestClass {
        public static void TestMethod() {
            var p = Process.GetCurrentProcess().Parent();
            Console.WriteLine("{0}", p.Id);
    
            var formatter = new CustomFormatter { };
            foreach(var x in Toolhelp32.TakeSnapshot<WinModuleEntry>(Toolhelp32.SnapModule, p.Id)) {
                Console.WriteLine(String.Format(formatter, "{0}", x));
            }
        }
    }
    
    public class CustomFormatter:IFormatProvider, ICustomFormatter {
        String ICustomFormatter.Format(String format, object arg, IFormatProvider formatProvider) {
            var type = arg.GetType();
            var fields = type.GetFields();
            var q = fields.Select(x => String.Format("{0}:{1}", x.Name, x.GetValue(arg)));
            return String.Format("{{{0}}}", String.Join(", ", q.ToArray()));
        }
    
        object IFormatProvider.GetFormat(Type formatType) {
            return typeof(ICustomFormatter)!=formatType ? null : this;
        }
    }
    

如果您想要一个代码示例..

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.