วิธีรับกระบวนการหลักใน. NET ด้วยวิธีการจัดการ


85

ฉันกำลังมองหาวิธีการรับกระบวนการหลักใน. NET เป็นจำนวนมาก แต่พบเพียงวิธี P / Invoke เท่านั้น


5
จะเกิดอะไรขึ้นเมื่อหลายอินสแตนซ์ของกระบวนการของคุณกำลังทำงานเนื่องจากทั้งหมดจะมี ProcessName เหมือนกัน
Michael Burr

1
ในกรณีที่มันช่วยคนอื่น: ฉันต้องการรหัสกระบวนการหลักเป็นการส่วนตัว วิธีแก้ปัญหาด้านล่างโดย Michael Hale และ Simon Mourier จะไม่ทำงานหากกระบวนการหลักได้ออกไปแล้วเนื่องจากพวกเขากำลังเรียกProcess.GetProcessById()ด้วย ID ของ ID กระบวนการที่ไม่มีอยู่จริง (ตอนนี้) แต่ ณ จุดนั้นคุณมีรหัสกระบวนการของผู้ปกครองดังนั้นคุณสามารถใช้รหัสนั้นได้หากต้องการเช่นเดียวกับฉัน
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กำหนดไว้ที่ไหน?
Mark Byers

22
นี่เป็นวิธีการที่มีชื่อไม่ดีอย่างน่าอัศจรรย์
มาร์ค

4
ในการทดสอบของฉันสิ่งนี้ช้ากว่าโซลูชันของ Simon Mourier มาก นอกจากนี้ยังน่าเสียดายที่กลไก 'นำกระบวนการไปข้างหน้า' บางประเภท ฉันไม่แน่ใจว่าทำไม มีใครมีประสบการณ์นี้อีกบ้าง? การทดสอบที่ฉันกำลังดำเนินการนี้คือการตั้งค่า bootstrapper EXE ที่สร้างโดย Visual Studio ที่เปิดตัวติดตั้ง windows MSIEXEC.exe
Tyler Collier

6
น่าเสียดายที่ไม่สามารถใช้งานได้เมื่อชื่อหมวดหมู่ตัวนับประสิทธิภาพเป็นภาษาท้องถิ่น (เช่นบน Windows ที่ไม่ใช่ภาษาอังกฤษ)
LukeSw

5
ฉันขอแนะนำเวอร์ชันของ Simon เว้นแต่จะมีเหตุผลเร่งด่วนที่จะไม่ทำเพราะความแตกต่างของประสิทธิภาพมีความสำคัญ
David Burton

151

นี่คือวิธีแก้ปัญหา มันใช้ 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 เองดังนั้นฉันจึงไม่คิดว่ามันจะเป็นปัญหาใหญ่
Simon Mourier

11
เยี่ยมมาก! ไม่มีเคาน์เตอร์สมบูรณ์แบบช้า ฉันเกลียดความคิดเห็นที่ "ไม่มีการจัดการ" จริงๆ วิธีการสอบถามตัวนับ perf มีการจัดการมากขึ้นจากนั้นใช้ P / Invoke อย่างไร
Jabe

5
น่าเสียดายที่ฟังก์ชันนี้เป็นแบบภายในเท่านั้น MSDN ระบุว่า "[NtQueryInformationProcess อาจมีการเปลี่ยนแปลงหรือไม่พร้อมใช้งานใน Windows เวอร์ชันอนาคตแอปพลิเคชันควรใช้ฟังก์ชันอื่นที่ระบุไว้ในหัวข้อนี้]" msdn.microsoft.com/en-us/library/windows/desktop/…
justin ม

21
@ justin.m.chase - อยู่ที่นั่นมาเกือบ 20 ปีแล้วดังนั้นฉันสงสัยว่ามันจะถูกลบออกในวันพรุ่งนี้และไม่มีฟังก์ชั่น NT แบบอัลเทนเนตที่ให้กระบวนการผู้ปกครองในความรู้ของฉัน แต่ใช่แน่นอนใช้ด้วยความเสี่ยงของคุณเอง .
Simon Mourier

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 วินาที
Karsten

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กับคำต่อท้ายด้วยความช่วยเหลือฉันคิดว่าคลาสผู้ช่วยแบบคงที่นั้นเหมาะสมดังนั้นเราจึงสามารถมีชื่อที่ตรงตามคุณสมบัติที่ชัดเจนเช่น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;
        }
    }
    

ในกรณีที่ต้องการตัวอย่างโค้ด ..

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.