管理された方法で.NETで親プロセスを取得する方法


85

.NETで親プロセスを取得する方法をたくさん探していましたが、P / Invokeの方法しか見つかりませんでした。


5
プロセスの複数のインスタンスがすべて同じProcessNameを持つため、実行中はどうなりますか?
Michael Burr

1
それが他の誰かを助ける場合:私は個人的に親プロセスIDだけを必要としていました。MichaelHaleとSimonMourierによる以下のソリューションProcess.GetProcessById()は、(現在)存在しないプロセスIDのIDで呼び出しているため、親プロセスが終了した場合は機能しません。ただし、その時点で親のプロセスIDを持っているので、必要に応じて、私と同じように使用できます。
タイラーコリアー2011年


親プロセスIDをコマンドライン引数として送信するのはどうですか?:)
John Demetriou 2016年

回答:


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定義されていますか?
マークバイアーズ2010

22
これらは、驚くほど名前が不十分なメソッドです。
マーク

4
私のテストでは、これはSimonMourierのソリューションよりもはるかに低速です。また、残念ながら、ある種の「プロセスを前面に出す」メカニズムを実行します。理由はわかりません。他の誰かがこれを経験しましたか?このために実行しているテストは、MSIEXEC.exeWindowsインストーラーを起動するVisualStudioによって作成されたセットアップブートストラップEXEです。
タイラーコリアー2011年

6
残念ながら、パフォーマンスカウンターのカテゴリ名がローカライズされている場合(英語以外のWindowsなど)は機能しません。
LukeSw 2012年

5
パフォーマンスの違いが大きいため、差し迫った理由がない限り、Simonのバージョンをお勧めします。
David Burton

151

これが解決策です。p / invokeを使用しますが、32または64cpuでうまく機能するようです。

    /// <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 2010

11
すごい!遅いパフォーマンスカウンターはありません。私は「管理されていない」コメントが本当に嫌いです。P / Invokeを使用するよりも、パフォーマンスカウンターのクエリをどのように管理しますか。
ジェイブ2011

5
残念ながら、この関数は内部のみです。MSDNには、これは「[。NtQueryInformationProcessは、Windowsの将来のバージョンで変更または使用不能にすることができるアプリケーションは、このトピックに記載されている代替機能を使用する必要があります。]」と言う msdn.microsoft.com/en-us/library/windows/desktop/...
ジャスティンを。 m.chase 2012年

21
@ justin.m.chase-ほぼ20年間存在しているので、明日削除されるとは思えません。また、親プロセスに私の知識を与える代替NT関数はありませんが、確かに、自己責任で使用してください。 。
Simon Mourier 2012年

4
この方法のパフォーマンスを他の方法と比較するとこの方法は少なくとも10倍高速です。受け入れられた答えはチェックします:2600657。この答えはチェックします:8454
Mojtaba Rezaeian 2016

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 2018年

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 2017年

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.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.