Functions/GenXdev.Windows/Get-ChildProcesses.cs

// ################################################################################
// Part of PowerShell module : GenXdev.Windows
// Original cmdlet filename : Get-ChildProcesses.cs
// Original author : René Vaessen / GenXdev
// Version : 1.302.2025
// ################################################################################
// Copyright (c) René Vaessen / GenXdev
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ################################################################################
 
 
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Management.Automation;
 
namespace GenXdev.Windows
{
    /// <summary>
    /// <para type="synopsis">
    /// Retrieves all processes that are descendants of the current PowerShell process.
    /// </para>
    ///
    /// <para type="description">
    /// Examines all running processes and identifies those that have the current
    /// PowerShell process as an ancestor in their parent process chain. This includes
    /// both direct child processes and their descendants (grandchildren, etc.).
    /// </para>
    ///
    /// <example>
    /// <para>Get all child processes of the current PowerShell session</para>
    /// <para>Demonstrates basic usage of the cmdlet to retrieve child processes.</para>
    /// <code>
    /// Get-ChildProcesses
    /// </code>
    /// </example>
    ///
    /// <example>
    /// <para>Get child processes and display verbose output</para>
    /// <para>Shows how to use the cmdlet with verbose output to see detailed information about the process detection.</para>
    /// <code>
    /// Get-ChildProcesses -Verbose
    /// </code>
    /// </example>
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "ChildProcesses")]
    [OutputType(typeof(Process))]
    public class GetChildProcessesCommand : PSGenXdevCmdlet
    {
        private int currentProcessId;
        private Dictionary<int, int> parentMap;
 
        /// <summary>
        /// Begin processing - initialization logic
        /// </summary>
        protected override void BeginProcessing()
        {
            // log start of process detection
            WriteVerbose("Starting child process detection...");
 
            // store current powershell process id for parent chain comparison
            currentProcessId = Process.GetCurrentProcess().Id;
            WriteVerbose($"Current process ID: {currentProcessId}");
 
            // build parent process map for efficient lookups
            parentMap = new Dictionary<int, int>();
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ProcessId, ParentProcessId FROM Win32_Process");
            foreach (ManagementObject obj in searcher.Get())
            {
                int pid = Convert.ToInt32(obj["ProcessId"]);
                int parentId = Convert.ToInt32(obj["ParentProcessId"]);
                parentMap[pid] = parentId;
            }
        }
 
        /// <summary>
        /// Process record - main cmdlet logic
        /// </summary>
        protected override void ProcessRecord()
        {
            // get all processes currently running on the system
            Process[] allProcesses = Process.GetProcesses();
            WriteVerbose($"Retrieved {allProcesses.Length} total processes");
 
            // filter processes by checking if current process is in their parent chain
            foreach (Process process in allProcesses)
            {
                if (IsDescendant(process.Id, currentProcessId))
                {
                    WriteVerbose($"Found child process: {process.ProcessName} ID: {process.Id}");
                    WriteObject(process);
                }
            }
        }
 
        /// <summary>
        /// End processing - cleanup logic
        /// </summary>
        protected override void EndProcessing()
        {
            WriteVerbose("Completed child process detection");
        }
 
        /// <summary>
        /// Check if a process is a descendant of the specified ancestor by traversing the parent chain
        /// </summary>
        /// <param name="processId">The process ID to check</param>
        /// <param name="ancestorId">The ancestor process ID to look for</param>
        /// <returns>True if the process is a descendant, false otherwise</returns>
        private bool IsDescendant(int processId, int ancestorId)
        {
            // traverse up the parent chain until we find our process or hit top
            while (processId != 0 && parentMap.ContainsKey(processId))
            {
                if (processId == ancestorId)
                {
                    return true;
                }
                processId = parentMap[processId];
            }
            return false;
        }
    }
}