관리 방식으로 .NET에서 부모 프로세스를 얻는 방법


85

.NET에서 부모 프로세스를 얻는 방법을 많이 찾고 있었지만 P / Invoke 방법 만 찾았습니다.


5
프로세스의 여러 인스턴스가 모두 동일한 ProcessName을 갖기 때문에 실행중인 경우 어떻게됩니까?
Michael Burr

1
다른 사람에게 도움이되는 경우 : 개인적으로 부모 프로세스 ID 만 필요했습니다. Michael Hale과 Simon Mourier의 아래 솔루션 Process.GetProcessById()은 (현재) 존재하지 않는 프로세스 ID의 ID로 호출하기 때문에 부모 프로세스가 종료 된 경우 작동하지 않습니다 . 하지만 그 시점에서 부모의 프로세스 ID가 있으므로 필요한 경우 저처럼 사용할 수 있습니다.
Tyler Collier


부모 프로세스 ID를 명령 줄 인수로 보내는 것은 어떻습니까? :)
존 데 메트

답변:


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
그것들은 놀랍게도 이름이 좋지 않은 방법입니다.
Mark

4
내 테스트에서 이것은 Simon Mourier의 솔루션보다 훨씬 느립니다. 또한 불행히도 일종의 '앞으로 가져 오기'메커니즘을 수행합니다. 이유를 모르겠습니다. 다른 사람이 이것을 경험 한 적이 있습니까? 내가 실행중인 테스트는 MSIEXEC.exe Windows 설치 프로그램을 시작하는 Visual Studio에서 만든 설치 부트 스트 래퍼 EXE입니다.
Tyler Collier

6
유감스럽게도 성능 카운터 범주 이름이 지역화 된 경우 (예 : 영어가 아닌 Windows에서) 작동하지 않습니다.
LukeSw 2012-06-05

5
성능 차이가 크므로 시급한 이유가 없다면 Simon의 버전을 제안합니다.
David Burton

150

여기에 해결책이 있습니다. 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 이외의 다른 OS에서는 이식 할 수 없습니다. 그러나 부모 프로세스의 개념은 .NET Framework 자체가 아니기 때문에 이식성이 없으므로 큰 문제라고 생각하지 않습니다.
Simon Mourier

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 기능은 없지만 예, 물론 사용자의 책임하에 사용하십시오. .
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 '메소드가 불필요하다는 것을 알았고, 그에 상응하는 엔트리 유형에 반복 메소드를 넣지 않는 이유를 생각했습니다. 구현하고 이해하는 것이 더 쉬울 것입니다 (그렇게 ..).

helpToolhelp32 접미사가 붙은 것처럼 정적 도우미 클래스가 적절하다고 생각하므로 여기에 관련이 없거나 관련이 없더라도 명확한 정규화 된 이름을 가질 수 있습니다.Toolhelp32.SnapshotToolhelp32.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.