Как получить родительский процесс в .NET управляемым способом


85

Я много искал способ получить родительский процесс в .NET, но нашел только способ P / Invoke.


5
Что происходит, когда выполняется несколько экземпляров вашего процесса, поскольку все они имеют одно и то же имя процесса?
Майкл Берр,

1
На случай, если это поможет кому-то другому: мне лично нужен только идентификатор родительского процесса. Приведенные ниже решения Майкла Хейла и Саймона Мурье не работают, если родительский процесс завершился, потому что они вызывают Process.GetProcessById()с идентификатором (теперь) несуществующего идентификатора процесса. Но в этот момент у вас есть идентификатор родительского процесса, поэтому вы можете использовать его, если он вам нужен, как и я.
Tyler Collier


Как насчет того, чтобы отправить идентификатор родительского процесса в качестве аргумента командной строки? :)
John Demetriou

Ответы:


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определяется метод ?
Марк Байерс

22
Это некоторые методы с удивительно неудачными названиями.
Марк

4
В моем тестировании это намного медленнее, чем решение Саймона Мурье. Кроме того, к сожалению, это своего рода механизм «вывести процесс на передний план». Не знаю почему. Кто-нибудь еще испытал это? Тест, который я выполняю для этого, - это установочный загрузчик EXE, созданный Visual Studio, который запускает установщик Windows MSIEXEC.exe.
Tyler Collier

6
К сожалению, это не работает, если имя категории счетчика производительности локализовано (например, в Windows, отличной от английского).
LukeSw 05

5
Я бы предложил версию Саймона, если нет веских причин не делать этого, потому что разница в производительности значительна.
Дэвид Бертон

151

Вот решение. Он использует p / invoke, но, похоже, работает хорошо, 32 или 64 процессора:

    /// <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/…
justin. m.chase

21
@ justin.m.chase - он существует уже почти 20 лет, поэтому я сомневаюсь, что он будет удален завтра, и нет альтернативных функций NT, которые передают родительский процесс, насколько мне известно, но да, конечно, используйте на свой страх и риск .
Саймон Мурье

4
Этот метод как минимум в 10 раз быстрее, если сравнивать его производительность с другими методами. Принятый ответ клещи: 2600657. Этот ответ клещей: 8454.
Mojtaba Rezaeian

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 Maw

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 .


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

Для альтернативной концовки ..

Согласно документации, существует пара итерационных методов для каждого типа записей, таких как Process32FirstиProcess32Next для итерации процессов; но я обнаружил, что методы `xxxxFirst 'не нужны, а затем я подумал, почему бы не поместить метод итерации с соответствующим типом записи? Было бы проще реализовать и понять (я так думаю ...).

Так же, как с Toolhelp32суффиксом help , я думаю, что статический вспомогательный класс является правильным, так что мы можем иметь четкие квалифицированные имена, такие как Toolhelp32.Snapshotили Toolhelp32.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;
        }
    }
    

Если вам нужен пример кода ..

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.