Finding the parent of a process in C#

If you want to find the parent process of a Windows process from .NET, there are a couple of alternatives. None of these alternatives is very straightforward, because this functionality is not a built into the System.Diagnostics.Process class. Well ... at least not by default. You can of course inject it via an extension method -say ParentProcess()- to enable calls like the following:

            // Find my own parent.
            Process myProcess = Process.GetCurrentProcess();
            Console.WriteLine(myProcess.ParentProcess().ProcessName);
 
            // Create a child, and find its parent.
            Process p = Process.Start("notepad.exe");
            Console.WriteLine(p.ParentProcess().ProcessName);


This code displays the parent of a test program, then starts a Notepad instance and requests its parent. The resulting console looks like this (with and without a debugger):

 

I've seen C# implementations that read performance counters or call WMI queries to get the parent process id, but I assume that the following extension method will outperform these:

//-----------------------------------------------------------------------
// <copyright file="ProcessExtensions.cs" company="DockOfTheBay">
//     http://www.dotbay.be
// </copyright>
// <summary>Defines the ProcessExtensions class.</summary>
//-----------------------------------------------------------------------
 
namespace DockOfTheBay
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
 
    /// <summary>
    /// Extension Methods for the System.Diagnostics.Process Class.
    /// </summary>
    public static class ProcessExtensions
    {
        /// <summary>
        /// Returns the Parent Process of a Process
        /// </summary>
        /// <param name="process">The Windows Process.</param>
        /// <returns>The Parent Process of the Process.</returns>
        public static Process ParentProcess(this Process process)
        {
            int parentPid = 0;
            int processPid = process.Id;
            uint TH32CS_SNAPPROCESS = 2;
 
            // Take snapshot of processes
            IntPtr hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 
            if (hSnapshot == IntPtr.Zero)
            {
                return null;
            }
 
            PROCESSENTRY32 procInfo = new PROCESSENTRY32();
 
            procInfo.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
 
            // Read first
            if (Process32First(hSnapshot, ref procInfo) == false)
            {
                return null;
            }
 
            // Loop through the snapshot
            do
            {
                // If it's me, then ask for my parent.
                if (processPid == procInfo.th32ProcessID)
                {
                    parentPid = (int)procInfo.th32ParentProcessID;
                }
            }
            while (parentPid == 0 && Process32Next(hSnapshot, ref procInfo)); // Read next
 
            if (parentPid > 0)
            {
                return Process.GetProcessById(parentPid);
            }
            else
            {
                return null;
            }
        }
 
        /// <summary>
        /// Takes a snapshot of the specified processes, as well as the heaps, 
        /// modules, and threads used by these processes.
        /// </summary>
        /// <param name="dwFlags">
        /// The portions of the system to be included in the snapshot.
        /// </param>
        /// <param name="th32ProcessID">
        /// The process identifier of the process to be included in the snapshot.
        /// </param>
        /// <returns>
        /// If the function succeeds, it returns an open handle to the specified snapshot.
        /// If the function fails, it returns INVALID_HANDLE_VALUE.
        /// </returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);
 
        /// <summary>
        /// Retrieves information about the first process encountered in a system snapshot.
        /// </summary>
        /// <param name="hSnapshot">A handle to the snapshot.</param>
        /// <param name="lppe">A pointer to a PROCESSENTRY32 structure.</param>
        /// <returns>
        /// Returns TRUE if the first entry of the process list has been copied to the buffer.
        /// Returns FALSE otherwise.
        /// </returns>
        [DllImport("kernel32.dll")]
        private static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
 
        /// <summary>
        /// Retrieves information about the next process recorded in a system snapshot.
        /// </summary>
        /// <param name="hSnapshot">A handle to the snapshot.</param>
        /// <param name="lppe">A pointer to a PROCESSENTRY32 structure.</param>
        /// <returns>
        /// Returns TRUE if the next entry of the process list has been copied to the buffer.
        /// Returns FALSE otherwise.</returns>
        [DllImport("kernel32.dll")]
        private static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
 
        /// <summary>
        /// Describes an entry from a list of the processes residing 
        /// in the system address space when a snapshot was taken.
        /// </summary>
        [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;
        }
    }
}

3 comments:

  1. thx, great code!!

    ReplyDelete
  2. Too bad you copyrighted it

    ReplyDelete
  3. You forgot to CloseHandle(hSnapshot)

    ReplyDelete