c# - Cómo obtener el proceso principal en.NET de forma administrada
process pinvoke (6)
Estaba buscando mucho método para obtener el proceso principal en .NET, pero solo encontré P / invocar.
Aquí está mi intento en una solución administrada.
Realiza una encuesta de los contadores de rendimiento para todos los procesos y devuelve un diccionario de PID secundario al PID principal. Luego puede consultar el diccionario con su PID actual para ver a su padre, abuelo, etc.
Es excesivo en la cantidad de información que se obtiene, seguro. Siéntase libre de optimizar.
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;
}
}
}
En otras noticias, aprendí cuántos contadores de rendimiento había en mi máquina: 13401. Santa vaca.
Aquí hay una solución. Utiliza p / invoke, pero parece funcionar bien, 32 o 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;
}
}
}
De esta manera:
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;
}
}
Este código proporciona una interfaz agradable para encontrar el objeto de proceso principal y tiene en cuenta la posibilidad de múltiples procesos con el mismo nombre:
Uso:
Console.WriteLine("ParentPid: " + Process.GetProcessById(6972).Parent().Id);
Código:
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));
}
}
Si acepta P / Invoke, hay una manera mejor, que está más documentada que NtQueryInformationProcess: es decir, PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). Se muestra en esta publicación .
Preste atención a los detalles sutiles y tenga en cuenta que el PID de los padres no es necesariamente el PID del creador, de hecho, estos pueden no estar relacionados por completo, como lo señalan los comentarios de la comunidad en PROCESSENTRY32 .
Si alguna vez ha extraído el BCL, encontrará que las formas de encontrar el proceso principal se evitan deliberadamente, tome esto por ejemplo:
Como puede ver en el código fuente, contiene estructuras integrales y métodos nativos importados que son absolutamente suficientes para realizar el trabajo. Sin embargo, incluso si accede a ellos mediante la reflexión (esto es posible), no encontrará un método para hacerlo directamente. No puedo responder por qué, sin embargo, este fenómeno hace que las preguntas como la tuya se hagan de manera repetida; por ejemplo:
¿Cómo puedo obtener el PID del proceso principal de mi aplicación?
Para que no haya respuesta junto con algún código que use CreateToolhelp32Snapshot en este hilo, lo agregaría: parte de las definiciones de estructura y los nombres que robo de la fuente de referencia de MS :)
Código
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); } }
Y podemos usarlo como:
Prueba
public class TestClass { public static void TestMethod() { var p = Process.GetCurrentProcess().Parent(); Console.WriteLine("{0}", p.Id); } }
Para final alternativo
De acuerdo con la documentación, hay un par de métodos de iteración por tipo de las entradas, tales como Process32First
y Process32Next
son para la iteración de procesos; pero encontré que los métodos `xxxxFirst ''son innecesarios, y entonces pensé ¿por qué no poner el método de iteración con su tipo de entrada correspondiente? Sería más fácil de implementar y entender (supongo que sí ...).
Al igual que Toolhelp32
sufijo con ayuda , creo que una clase de ayuda estática es adecuada, de modo que podemos tener los nombres claramente calificados como Toolhelp32.Snapshot
o Toolhelp32.IEntry
aunque sería irrelevante aquí ...
Una vez que se obtiene el proceso principal, si además desea obtener algunas informaciones detalladas, puede extender esto fácilmente, por ejemplo, iterar en sus módulos, y luego agregar:
Código - 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; }
y alguna prueba ...
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; } }
En caso de que quiera un ejemplo de código ...