كيفية الحصول على العملية الأصل في .NET في الطريقة المدارة

StackOverflow https://stackoverflow.com/questions/394816

سؤال

كنت أبحث كثيرا عن طريقة للحصول على عملية الوالد في .NET، ولكن وجدت طريقة P / Invoke فقط.

هل كانت مفيدة؟

المحلول

يوفر هذا الرمز واجهة جميلة للعثور على كائن العملية الأصل ويأخذ في الاعتبار إمكانية العمليات المتعددة بنفس الاسم:

الاستعمال:

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

نصائح أخرى

هنا هو الحل. يستخدم 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;
            }
        }
    }

من هنا:

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

ها هي جربتي في حل مدار.

يستند استدعاء عدادات الأداء لجميع العمليات وإرجاع قاموس الطفل 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. البقرة المقدسة.

إذا قبول P / Invoke، فهناك طريقة أفضل، وهي موثقة أكثر من NTQueryInformationProcess: وهي processentry32 (createToolhelp32snapshot، process32first، process32next). إنه يظهر في هذا المنصب.

الانتباه إلى التفاصيل الدقيقةولاحظ أن PID الوالدين ليس بالضرورة PID الخالق، في الواقع قد تكون هذه غير ذات صلة بالكامل، كما أشار من قبل تعليقات المجتمع فيmoryentry32..

إذا قمت بإنقاذ BCL، فستجد أن الطرق للعثور على العملية الأصل تدرج عمدا، خذ هذا على سبيل المثال:

https://referencationsource.microsoft.com/#shystem/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 معاجه مع مساعدة, أعتقد أن فئة المساعد الثابتة مناسبة، حتى نتمكن من الحصول على أسماء مؤهلة واضحة مثل 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;
        }
    }
    

في حال كنت تريد مثال على التعليمات البرمجية ..

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top