Cách lấy quy trình mẹ trong .NET theo cách được quản lý


85

Tôi đã tìm kiếm rất nhiều phương pháp để lấy quy trình cha trong .NET, nhưng chỉ tìm thấy cách P / Invoke.


5
Điều gì sẽ xảy ra khi nhiều phiên bản trong quy trình của bạn đang chạy, vì tất cả chúng sẽ có cùng một ProcessName?
Michael Burr

1
Trong trường hợp nó giúp người khác: Cá nhân tôi chỉ cần ID quy trình gốc. Các giải pháp dưới đây của Michael Hale và Simon Mourier không hoạt động nếu quy trình mẹ đã thoát vì chúng đang gọi Process.GetProcessById()với ID của một ID quy trình (hiện tại) không tồn tại. Nhưng tại thời điểm đó, bạn có ID quy trình của cha mẹ, vì vậy bạn có thể sử dụng ID đó nếu bạn cần như tôi đã làm.
Tyler Collier


Làm thế nào về việc bạn gửi id quy trình gốc dưới dạng đối số dòng lệnh? :)
John Demetriou

Câu trả lời:


62

Đoạn mã này cung cấp một giao diện đẹp để tìm kiếm đối tượng Parent process và có tính đến khả năng có nhiều process có cùng tên:

Sử dụng:

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

Mã:

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
Phương thức float.Asđược định nghĩa ở đâu?
Mark Byers

22
Đó là một số phương pháp được đặt tên kém đáng kinh ngạc.
Đánh dấu

4
Trong thử nghiệm của tôi, điều này chậm hơn nhiều so với giải pháp của Simon Mourier. Ngoài ra, nó không may thực hiện một số loại cơ chế 'đưa quá trình lên phía trước'. Tôi cung không chăc tại sao. Đã có ai thử điều này chưa? Bài kiểm tra tôi đang chạy cho đây là một khởi động thiết lập EXE được tạo bởi Visual Studio để khởi chạy trình cài đặt cửa sổ MSIEXEC.exe.
Tyler Collier

6
Rất tiếc, nó không hoạt động khi tên danh mục bộ đếm hiệu suất được bản địa hóa (ví dụ: trên Windows không phải tiếng Anh).
LukeSw

5
Tôi muốn đề xuất phiên bản của Simon trừ khi có lý do cấp bách để không làm vậy, bởi vì sự khác biệt về hiệu suất là đáng kể.
David Burton

150

Đây là một giải pháp. Nó sử dụng p / invoke, nhưng dường như hoạt động tốt, 32 hoặc 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
Nó thực sự được quản lý, nhưng không di động trên một hệ điều hành khác ngoài Windows, bạn nói đúng. Tuy nhiên, khái niệm về quy trình mẹ cũng không di động được, vì bản thân nó không nằm trong .NET Framework, vì vậy tôi không nghĩ đó là một vấn đề lớn.
Simon Mourier

11
Tuyệt quá! Không có bộ đếm hiệu suất chậm. Tôi thực sự ghét những bình luận "không được quản lý". Làm thế nào để truy vấn một bộ đếm hiệu suất được quản lý nhiều hơn sau đó sử dụng P / Invoke.
Jabe

5
Thật không may, chức năng này chỉ dành cho nội bộ. MSDN cho biết điều này "[NtQueryInformationProcess có thể bị thay đổi hoặc không khả dụng trong các phiên bản Windows trong tương lai. Các ứng dụng nên sử dụng các chức năng thay thế được liệt kê trong chủ đề này.]" Msdn.microsoft.com/en-us/library/windows/desktop/…
justin. m.chase

21
@ justin.m.chase - Nó đã ở đó gần 20 năm, vì vậy tôi nghi ngờ nó sẽ bị xóa vào ngày mai và không có chức năng NT thay đổi nào cung cấp cho quy trình gốc mà tôi biết, nhưng vâng, chắc chắn, sử dụng và tự chịu rủi ro .
Simon Mourier

4
Phương pháp này nhanh hơn ít nhất 10 lần khi tôi so sánh hiệu suất của phương pháp này với các phương pháp khác. Câu trả lời được chấp nhận ticks: 2600657. Câu trả lời này ticks: 8454.
Mojtaba Rezaeian

9

Cách này:

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
Hoạt động, nhưng WMI có thể siêu chậm (giây) .pinvoke là cách để đi.
Alastair Maw

4

Đây là thử của tôi tại một giải pháp được quản lý.

Nó thăm dò các bộ đếm hiệu suất cho tất cả các quy trình và trả về từ điển PID con cho PID mẹ. Sau đó, bạn có thể kiểm tra từ điển với PID hiện tại của mình để xem cha mẹ, ông bà của bạn, v.v.

Chắc chắn là nó quá mức cần thiết về lượng thông tin mà nó nhận được. Hãy thoải mái tối ưu hóa.

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

Trong một tin khác, tôi đã biết được có bao nhiêu bộ đếm hiệu suất trên máy của mình: 13401. Chúa ơi.


2
Phương pháp này hoạt động nhưng dường như cực kỳ chậm. Mất hơn 10 giây trong máy của tôi.
Karsten

3

Nếu chấp nhận P / Invoke, có một cách tốt hơn, được tài liệu hóa nhiều hơn NtQueryInformationProcess: cụ thể là PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). Nó được hiển thị trong bài đăng này .

Hãy chú ý đến các chi tiết tinh tế và lưu ý rằng PID gốc không nhất thiết phải là PID của người tạo, trên thực tế, những điều này có thể hoàn toàn không liên quan, như được chỉ ra bởi các bình luận của cộng đồng tại PROCESSENTRY32 .


2

Nếu bạn đã từng đào BCL, bạn sẽ thấy rằng các cách để tìm quy trình mẹ được cố tình tránh, lấy ví dụ như sau:

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

Như bạn có thể thấy trong mã nguồn, nó chứa các cấu trúc toàn diện và các phương thức gốc được nhập hoàn toàn đủ để hoàn thành công việc. Tuy nhiên, ngay cả khi bạn truy cập chúng thông qua phản chiếu (điều này có thể xảy ra), bạn sẽ không tìm thấy phương pháp thực hiện trực tiếp. Tôi không thể trả lời tại sao, nhưng hiện tượng này khiến những câu hỏi như của bạn được đặt ra nhiều lần; ví dụ:

Làm cách nào tôi có thể lấy PID của quy trình chính của đơn đăng ký của tôi

Vì không có câu trả lời cùng với một số mã sử dụng CreateToolhelp32Snapshot trong chủ đề này, tôi sẽ thêm nó vào - một phần của định nghĩa cấu trúc và tên mà tôi lấy cắp từ nguồn tham khảo của 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);
        }
    }
    

Và chúng ta có thể sử dụng nó như:

  • Kiểm tra

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

Đối với kết thúc thay thế ..

Theo tài liệu, có một cặp phương pháp lặp cho mỗi loại mục nhập, chẳng hạn như Process32FirstProcess32Nextdành cho việc lặp lại các quy trình; nhưng tôi thấy các phương thức `xxxxFirst 'là không cần thiết và sau đó tôi nghĩ tại sao không đặt phương thức lặp với loại mục nhập tương ứng của nó? Nó sẽ dễ thực hiện và dễ hiểu hơn (tôi đoán vậy ..).

Cũng giống như Toolhelp32hậu tố với sự giúp đỡ , tôi nghĩ rằng một lớp helper tĩnh là thích hợp, vì vậy chúng tôi có thể có tên tiêu chuẩn rõ ràng như Toolhelp32.Snapshothay Toolhelp32.IEntrymặc dù nó muốn được liên quan ở đây ..

Sau khi có được quy trình gốc, nếu bạn muốn nhận thêm một số thông tin chi tiết, bạn có thể mở rộng với quy trình này một cách dễ dàng, ví dụ: lặp lại trên các mô-đun của nó, sau đó thêm:

  • Mã - 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;
    }
    

    và một số bài kiểm tra ..

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

Trong trường hợp bạn muốn một ví dụ về mã ..

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.