Functions/GenXdev.Windows/Get-ChildProcesses.cs

// ################################################################################
// Part of PowerShell module : GenXdev.Windows
// Original cmdlet filename : Get-ChildProcesses.cs
// Original author : René Vaessen / GenXdev
// Version : 3.26.2026
// ################################################################################
// Copyright (c) 2026 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.Diagnostics;
using System.Management;
using System.Management.Automation;

namespace GenXdev.Windows
{
    [System.ComponentModel.Description(@"
.SYNOPSIS
Retrieves all processes that are descendants of the current PowerShell
process.
.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.).
.EXAMPLE
```powershell
Get-ChildProcesses
```

Get all child processes of the current PowerShell session.
.EXAMPLE
```powershell
Get-ChildProcesses -Verbose
```

Get child processes with verbose output showing detailed process detection
information.
")]
    [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;
        }
    }
}