NET'te üst süreç yönetilen bir şekilde nasıl elde edilir


85

.


5
Hepsi aynı ProcessName'e sahip olacağından, işleminizin birden çok örneği çalışırken ne olur?
Michael Burr

1
Başkasına yardımcı olması durumunda: Kişisel olarak sadece ana süreç kimliğine ihtiyacım vardı. Michael Hale ve Simon Mourier'in aşağıdaki çözümleri Process.GetProcessById(), (şimdi) var olmayan bir işlem kimliğinin kimliğiyle aradıkları için üst süreç çıkmışsa işe yaramaz . Ama bu noktada ebeveynin süreç kimliğine sahip olursunuz, böylece benim yaptığım gibi ihtiyacınız olursa bunu kullanabilirsiniz.
Tyler Collier


Üst işlem kimliğini komut satırı bağımsız değişkeni olarak göndermeye ne dersiniz? :)
John Demetriou

Yanıtlar:


62

Bu kod, Ana işlem nesnesini bulmak için güzel bir arayüz sağlar ve aynı ada sahip birden fazla işlem olasılığını hesaba katar:

Kullanım:

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

Kod:

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
Yöntem nerede float.Astanımlanır?
Mark Byers

22
Bunlar şaşırtıcı derecede kötü adlandırılmış bazı yöntemlerdir.
Mark

4
Benim testlerimde, bu Simon Mourier'in çözümünden çok daha yavaş. Ayrıca maalesef bir çeşit 'süreci öne getir' mekanizması da var. Neden olduğundan emin değilim. Bunu başka biri deneyimledi mi? Bunun için çalıştırdığım test, MSIEXEC.exe Windows yükleyicisini başlatan, Visual Studio tarafından oluşturulan bir kurulum önyükleyici EXE'sidir.
Tyler Collier

6
Ne yazık ki performans sayacı kategori adı yerelleştirildiğinde çalışmaz (örneğin, İngilizce olmayan Windows'ta).
LukeSw

5
Olmaması için acil bir neden olmadıkça Simon'ın versiyonunu öneririm, çünkü performans farkı önemli.
David Burton

151

İşte bir çözüm. P / invoke kullanıyor, ancak iyi çalışıyor gibi görünüyor, 32 veya 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
Aslında yönetiliyor, ancak haklı olduğunuz Windows'tan başka bir işletim sisteminde taşınabilir değil. Ancak, .NET Framework'te olmadığı için ana süreç kavramı da taşınabilir değildir, bu yüzden bunun büyük bir sorun olduğunu düşünmüyorum.
Simon Mourier

11
Harika! Yavaş performans sayacı yok. "Yönetilmeyen" yorumlarından gerçekten nefret ediyorum. Bir performans sayacını sorgulamak, P / Invoke kullanmaktan daha iyi yönetilebilir.
Jabe

5
Ne yazık ki bu işlev yalnızca dahili. MSDN, "[NtQueryInformationProcess, Windows'un gelecekteki sürümlerinde değiştirilebilir veya mevcut olmayabilir. Uygulamalar bu başlıkta listelenen alternatif işlevleri kullanmalıdır.]" Msdn.microsoft.com/en-us/library/windows/desktop/…
justin. m.chase

21
@ justin.m.chase - Neredeyse 20 yıldır oradaydı, bu yüzden yarın kaldırılacağından şüpheliyim ve ana süreci benim bilgime veren başka NT işlevleri yok, ama evet, elbette, kendi sorumluluğunuzda kullanın .
Simon Mourier

4
Bu yöntemin performansını diğer yöntemlerle karşılaştırdığımda bu yöntem en az 10 kat daha hızlı . Kabul edilen cevap keneler: 2600657. Bu cevap keneler: 8454.
Mojtaba Rezaeian

9

Bu yoldan:

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
Çalışır, ancak WMI süper yavaş (saniyeler) olabilir. Pinvoke gitmenin yoludur.
Alastair Maw

4

İşte yönetilen bir çözüm denemem.

Tüm işlemler için performans sayaçlarını sorgular ve ana PID'ye bir çocuk PID sözlüğü döndürür. Daha sonra ebeveyninizi, büyük ebeveyninizi vb. Görmek için mevcut PID'nizle sözlüğü kontrol edebilirsiniz.

Elbette ne kadar bilgi aldığı konusunda aşırıya kaçıyor. Optimize etmekten çekinmeyin.

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

Diğer bir haberde, makinemde kaç tane performans sayacı olduğunu öğrendim: 13401. Kutsal inek.


2
Bu yöntem işe yarıyor ama son derece yavaş görünüyor. Makinemde 10 saniyeden fazla sürdü.
Karsten

3

P / Invoke'u kabul ediyorsanız, NtQueryInformationProcess'ten daha fazla belgelenmiş olan daha iyi bir yol vardır: PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). O oluyor bu yazı gösterilen .

Öde dikkat ince ayrıntılar ve ebeveyn PID Bunlara toplum yorumlara göre sivri out gibi, tamamen ilgisiz olabilir aslında yaratıcısı PID, ille olmadığını not PROCESSENTRY32 .


2

BCL'yi daha önce kazdıysanız, ana süreci bulmanın yollarından kasıtlı olarak kaçınıldığını göreceksiniz, örneğin şunu alın:

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

Kaynak kodda görebileceğiniz gibi, kapsamlı yapılar ve işin yapılması için kesinlikle yeterli olan içe aktarılmış yerel yöntemler içerir. Ancak, onlara yansıtma yoluyla erişseniz bile (bu mümkündür), bunu doğrudan yapmak için bir yöntem bulamazsınız. Nedenini cevaplayamıyorum, ancak bu fenomen sizinki gibi soruların bir şekilde tekrar tekrar sorulmasına neden oluyor; Örneğin:

Başvurumun ana sürecinin PID'sini nasıl alabilirim

Bu iş parçacığında CreateToolhelp32Snapshot kullanan bazı kodların yanı sıra bir cevap olmadığı için , onu eklerim - MS'nin referans kaynağından çaldığım yapı tanımlarının ve adlarının bir parçası :)

  • Kod

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

Ve bunu şu şekilde kullanabiliriz:

  • Ölçek

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

Alternatif son için ..

Belgelere göre, her girdi türü için işlemlerin yinelenmesi gibi Process32Firstve Process32Nextbunlar için bir çift yineleme yöntemi vardır; ancak "xxxxFirst 'yöntemlerinin gereksiz olduğunu buldum ve sonra neden yineleme yöntemini karşılık gelen girdi türüyle birlikte koymadığımı düşündüm? Uygulaması ve anlaşılması daha kolay olurdu (sanırım öyle ..).

Tıpkı Toolhelp32ile sonlandırılmış yardımı , bir statik yardımcı sınıfı biz gibi berrak nitelikli isimlere sahip olabilir böylece, doğru olduğunu düşünüyorum Toolhelp32.Snapshotya Toolhelp32.IEntryda burada alakasız olurdu gerçi ..

Üst süreç elde edildikten sonra, bazı ayrıntılı bilgileri daha fazla almak istiyorsanız, bununla kolayca genişletebilirsiniz, örneğin, modüllerini yineleyin ve sonra ekleyin:

  • Kod - 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;
    }
    

    ve biraz test ..

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

Bir kod örneği istemeniz durumunda ..

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.