Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to make sure a background process spawned by my program is killed when my process terminates?

Basically the child process runs indefinitely until killed in the background, and I want to clean it up when my program terminates for any reason, i.e. via the Taskmanager.

Currently I have a while (Process.GetProcessesByName("ParentProcess").Count() > 0) loop and exit if the parent process isn't running, but it seems pretty brittle, and if I wanted it to work under debugger in Visual Studio I'd have to add "ParentProcess.vshost" or something.

Is there any way to make sure that the child process end without requiring the child process to know about the parent process? I'd prefer a solution in managed code, but if there isn't one I can PInvoke.

Edit: Passing the PID seems like a more robust solution, but for curiosity's sake, what if the child process was not my code but some exe that I have no control over? Is there a way to safeguard against possibly creating orphaned child processes?

like image 209
Davy8 Avatar asked Mar 10 '09 20:03

Davy8


2 Answers

If the child process is your own code, you could pass it the PID of the parent process when you launch it. The child process could then fetch the process with Process.GetProcessById and subscribe to its Exited event with a handler which shuts down the rest of the (child) process gracefully. Note that you need to set the EnableRaisingEvents property on the process to true.

like image 198
Jon Skeet Avatar answered Sep 28 '22 07:09

Jon Skeet


If the child process is not your own code you can use this code to find and kill all child processes:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Util {
    public static class ProcessExtensions {
        public static void KillDescendants(this Process processToNotKillYet) {
            foreach (var eachProcess in Process.GetProcesses()) {
                if (eachProcess.ParentPid() == processToNotKillYet.Id) {
                    eachProcess.KillTree();
                }
            }
        }

        public static void KillTree(this Process processToKill) {
            processToKill.KillDescendants();
            processToKill.Kill();
        }

        public static PROCESS_BASIC_INFORMATION Info(this Process process) {
            var processInfo = new PROCESS_BASIC_INFORMATION();
            try {
                uint bytesWritten;
                NtQueryInformationProcess(process.Handle,
                                          0,
                                          ref processInfo,
                                          (uint)Marshal.SizeOf(processInfo),
                                          out bytesWritten); // == 0 is OK
            }
            catch (Win32Exception e) {
                if (!e.Message.Equals("Access is denied")) throw;
            }

            return processInfo;
        }

        public static int ParentPid(this Process process) {
            return process.Info().ParentPid;
        }

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(
            IntPtr hProcess,
            int processInformationClass /* 0 */,
            ref PROCESS_BASIC_INFORMATION processBasicInformation,
            uint processInformationLength,
            out uint returnLength);

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_BASIC_INFORMATION {
            public int ExitStatus;
            public int PebBaseAddress;
            public int AffinityMask;
            public int BasePriority;
            public int Pid;
            public int ParentPid;
        }
    }
}
like image 45
Alan Hensel Avatar answered Sep 28 '22 07:09

Alan Hensel