开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
注意:父进程并一定是进程的创建者,这一点要注意。
到 .NET 6 为止,似乎还没有获取父进程或子进程的跨平台的托管代码解决方案。更多参见Expose Parent/Child Process Information via System.Diagnostics.Process (#24423)介绍。
获取父进程
使用WMI
/// <summary>
/// 获取父进程。如果出错可能返回null
/// </summary>
/// <param name="process"></param>
/// <returns></returns>
public static Process GetParent(this Process process)
{
try
{
//using (var query = new ManagementObjectSearcher("SELECT * FROM Win32_Process WHERE ProcessId=" + process.Id))
using (var query = new ManagementObjectSearcher("root\\CIMV2", "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId=" + process.Id))
{
return query
.Get()
.OfType<ManagementObject>()
.Select(p => Process.GetProcessById((int)(uint)p["ParentProcessId"]))
.FirstOrDefault();
}
}
catch
{
return null;
}
}
或
/// <summary>
/// 获取一个进程的父进程Id
/// </summary>
/// <param name="processId"></param>
/// <returns></returns>
public static int ParentProcessId(int processId)
{
try
{
using (var query = new ManagementObjectSearcher("root\\CIMV2", "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId=" + processId))
{
return query
.Get()
.OfType<ManagementObject>()
.Select(p =>(int)(uint)p["ParentProcessId"])
.FirstOrDefault();
}
}
catch
{
return 0;
}
}
或者,不使用linq查询的方式
/// <summary>
/// 获取父进程第2种方法
/// </summary>
/// <param name="process"></param>
/// <returns></returns>
public static Process GetParent2(this Process process)
{
try
{
var queryStr = string.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", process.Id);
using (var search = new ManagementObjectSearcher("root\\CIMV2", queryStr))
{
var results = search.Get().GetEnumerator();
results.MoveNext(); // 移动到下一个,并指示是否已经移动到了枚举的下一个对象。不执行,下面的获取将会报错。【应该是从未指向对象,移动到第一个】
var queryObj = results.Current;
var parentId = (uint)queryObj["ParentProcessId"];
return Process.GetProcessById((int)parentId);
}
}
catch
{
return null;
}
}
使用系统内核函数 NtQueryInformationProcess/NtQueryInformationProcess64
NtQueryInformationProcess
相对来说是一个性能较好的方法,但是MSDN官方文档也提到:[NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.] —— NtQueryInformationProcess function
因此可以考虑更换替代方法:CheckRemoteDebuggerPresent 和 GetProcessId
同时,如果遇到32位和64位的问题,可以参考使用NtQueryInformationProcess64
(文档中未记录的方法)
/// <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;
}
}
}
同时,This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.
提到需要使用LoadLibrary 和 GetProcAddress 加载,但是上面的DllImport
,测试可以正常运行。如果有问题,可以考虑使用这两个方法
使用 CreateToolhelp32Snapshot 等内核函数、PROCESSENTRY32结构
CreateToolhelp32Snapshot
获取进程、堆、模块或者线程等信息的快照。是一种工具方法,相对上面的方法,会安全方便些。
相对更推荐。
CreateToolhelp32Snapshot
:用于获取进程、堆、模块或者线程等信息的快照。 拿到的不同快照信息需要使用对应函数去遍历。
/// <summary>
/// PInvoke获取父进程id 未测试
/// </summary>
/// <param name="process"></param>
/// <returns></returns>
public static int ParentProcessId_PInvoke(this Process process)
{
return ParentProcessId_PInvoke(process.Id);
}
/// <summary>
/// PInvoke获取父进程id 未测试
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public static int ParentProcessId_PInvoke(int Id)
{
PROCESSENTRY32 pe32 = new PROCESSENTRY32 { };
pe32.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
using (var hSnapshot = CreateToolhelp32Snapshot(SnapshotFlags.Process, (uint)Id))
{
if (hSnapshot.IsInvalid)
throw new Win32Exception();
if (!Process32First(hSnapshot, ref pe32))
{
int errno = Marshal.GetLastWin32Error();
if (errno == ERROR_NO_MORE_FILES)
return -1;
throw new Win32Exception(errno);
}
do
{
if (pe32.th32ProcessID == (uint)Id)
return (int)pe32.th32ParentProcessID;
} while (Process32Next(hSnapshot, ref pe32));
}
return -1;
}
private const int ERROR_NO_MORE_FILES = 0x12;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeSnapshotHandle CreateToolhelp32Snapshot(SnapshotFlags flags, uint id);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool Process32First(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool Process32Next(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
[Flags]
private enum SnapshotFlags : uint
{
HeapList = 0x00000001,
Process = 0x00000002,
Thread = 0x00000004,
Module = 0x00000008,
Module32 = 0x00000010,
All = (HeapList | Process | Thread | Module),
Inherit = 0x80000000,
NoHeaps = 0x40000000
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
};
[SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
internal sealed class SafeSnapshotHandle : SafeHandleMinusOneIsInvalid
{
internal SafeSnapshotHandle() : base(true)
{
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeSnapshotHandle(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
protected override bool ReleaseHandle()
{
return CloseHandle(base.handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern bool CloseHandle(IntPtr handle);
}
通过性能计数器获取
参考的 stackoverflow 中也给出了,通过性能计数器获取所有子父进程id的字典,从而获取父进程祖父进程id、获取父进程的办法。
但是,似乎有着很多限制,比如性能很差、本地化的版本中无法使用、用户需在性能用户组中等等。具体未进行测试,后续再了解。
获取子进程
仅获取子进程
/// <summary>
/// 仅获取子进程id
/// </summary>
/// <param name="pid">需要结束的进程ID</param>
public static int[] GetChildrenProcessIds(int pid)
{
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select ProcessID From Win32_Process Where ParentProcessID=" + pid))
{
ManagementObjectCollection moc = searcher.Get();
var childPids = new int[moc.Count];
var i = 0;
foreach (ManagementObject mo in moc)
{
childPids[i]= Convert.ToInt32(mo["ProcessID"]);
i++;
}
return childPids;
}
}