Comment obtenir le processus parent dans .NET de manière gérée

Je cherchais beaucoup la méthode pour obtenir le processus parent dans .NET, mais je n’ai trouvé que la méthode P / Invoke.

Ce code fournit une interface agréable pour trouver l’object de processus parent et prend en compte la possibilité de plusieurs processus portant le même nom:

Usage:

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

Code:

 public static class ProcessExtensions { private static ssortingng FindIndexedProcessName(int pid) { var processName = Process.GetProcessById(pid).ProcessName; var processesByName = Process.GetProcessesByName(processName); ssortingng 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)); } } 

Voici une solution. Il utilise p / invoke, mais semble fonctionner correctement, cpu 32 ou 64:

  ///  /// A utility class to determine a process parent. ///  [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); ///  /// Gets the parent process of the current process. ///  /// An instance of the Process class. public static Process GetParentProcess() { return GetParentProcess(Process.GetCurrentProcess().Handle); } ///  /// Gets the parent process of specified process. ///  /// The process id. /// An instance of the Process class. public static Process GetParentProcess(int id) { Process process = Process.GetProcessById(id); return GetParentProcess(process.Handle); } ///  /// Gets the parent process of a specified process. ///  /// The process handle. /// An instance of the Process class. 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; } } } 

Par ici:

 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() .Select(p => Process.GetProcessById((int)(uint)p["ParentProcessId"])) .FirstOrDefault(); } } catch { return null; } } 

Voici mon essai d’une solution gérée.

Il interroge les compteurs de performance pour tous les processus et renvoie un dictionnaire du PID enfant au PID parent. Ensuite, vous pouvez vérifier le dictionnaire avec votre PID actuel pour voir votre parent, grand-parent, etc.

Il est exagéré dans la quantité d’informations dont il dispose, c’est certain. N’hésitez pas à optimiser.

 using System; using System.Collections.Generic; using System.Diagnostics; namespace PidExamples { class ParentPid { static void Main(ssortingng[] 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 GetAllProcessParentPids() { var childPidToParentPid = new Dictionary(); var processCounters = new SortedDictionary(); 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(ssortingng 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; } } } 

Dans d’autres nouvelles, j’ai appris combien de compteurs de performance il y avait sur ma machine: 13401. Holy cow.

Si vous acceptez P / Invoke, il existe une meilleure méthode, plus documentée que NtQueryInformationProcess: PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). C’est affiché dans cet article .

Faites attention aux détails subtils et notez que le PID parent n’est pas nécessairement le PID créateur, en fait, ils peuvent ne pas avoir de lien, comme indiqué par les commentaires de la communauté sur PROCESSENTRY32 .

Si vous avez déjà trouvé la BCL, vous constaterez que les façons de trouver le processus parent sont délibérément évitées, prenez ceci par exemple:

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

Comme vous pouvez le voir dans le code source, il contient des structures complètes et des méthodes natives imscopes qui sont absolument suffisantes pour accomplir le travail. Cependant, même si vous y accédez par reflection (cela est possible), vous ne trouverez pas de méthode pour le faire directement. Je ne peux pas répondre à la question, mais ce phénomène provoque des questions comme la vôtre sont demandées à plusieurs resockets; par exemple:

Comment puis-je obtenir le PID du processus parent de mon application

Car il n’y a pas de réponse avec un code utilisant CreateToolhelp32Snapshot dans ce thread, je l’appendais – une partie des définitions de structure et des noms que je vole de la source de référence de MS 🙂

  • Code

     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 TakeSnapshot(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 Ssortingng fileName; //byte fileName[260]; //public const int sizeofFileName = 260; } 

     public static class Extensions { public static Process Parent(this Process p) { var ensortinges = Toolhelp32.TakeSnapshot(Toolhelp32.SnapAll, 0); var parentid = ensortinges.First(x => x.th32ProcessID==p.Id).th32ParentProcessID; return Process.GetProcessById(parentid); } } 

Et nous pouvons l’utiliser comme:

  • Tester

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

Pour fin alternative

Selon la documentation, il existe une paire de méthodes d’itération par type d’entrées telles que Process32First et Process32Next pour l’itération des processus; mais j’ai trouvé que les méthodes `xxxxFirst ‘n’étaient pas nécessaires, et j’ai alors pensé pourquoi ne pas mettre la méthode d’itération avec le type d’entrée correspondant? Ce serait plus facile à mettre en œuvre et à comprendre (je suppose…).

Tout comme Toolhelp32 avec une aide , je pense qu’une classe d’assistance statique est correcte, de sorte que nous pouvons avoir des noms clairement définis tels que Toolhelp32.Snapshot ou Toolhelp32.IEntry bien que cela ne soit pas pertinent ici.

Une fois que le processus parent est obtenu, si vous souhaitez en outre obtenir des informations détaillées, vous pouvez étendre facilement ce processus, par exemple, itérer sur ses modules, puis append:

  • Code – 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 ssortingng moduleName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public ssortingng fileName; //byte fileName[260]; //public const int sizeofModuleName = 256; //public const int sizeofFileName = 260; } 

    et quelques tests ..

     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(Toolhelp32.SnapModule, p.Id)) { Console.WriteLine(Ssortingng.Format(formatter, "{0}", x)); } } } public class CustomFormatter:IFormatProvider, ICustomFormatter { Ssortingng ICustomFormatter.Format(Ssortingng format, object arg, IFormatProvider formatProvider) { var type = arg.GetType(); var fields = type.GetFields(); var q = fields.Select(x => Ssortingng.Format("{0}:{1}", x.Name, x.GetValue(arg))); return Ssortingng.Format("{{{0}}}", Ssortingng.Join(", ", q.ToArray())); } object IFormatProvider.GetFormat(Type formatType) { return typeof(ICustomFormatter)!=formatType ? null : this; } } 

Si vous voulez un exemple de code ..