71

In C++ Windows app, I launch several long running child processes (currently I use CreateProcess(...) to do this.

I want the child processes to be automatically closed if my main processes crashes or is closed.

Because of the requirement that this needs to work for a crash of the "parent", I believe this would need to be done using some API/feature of the operating system. So that all the "child" processes are cleaned up.

How do I do this?

tshepang
  • 12,111
  • 21
  • 91
  • 136
jm.
  • 23,422
  • 22
  • 79
  • 93

7 Answers7

86

The Windows API supports objects called "Job Objects". The following code will create a "job" that is configured to shut down all processes when the main application ends (when its handles are cleaned up). This code should only be run once.:

HANDLE ghJob = CreateJobObject( NULL, NULL); // GLOBAL
if( ghJob == NULL)
{
    ::MessageBox( 0, "Could not create job object", "TEST", MB_OK);
}
else
{
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };

    // Configure all child processes associated with the job to terminate when the
    jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    if( 0 == SetInformationJobObject( ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
    {
        ::MessageBox( 0, "Could not SetInformationJobObject", "TEST", MB_OK);
    }
}

Then when each child process is created, execute the following code to launch each child each process and add it to the job object:

STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;

// Launch child process - example is notepad.exe
if (::CreateProcess( NULL, "notepad.exe", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
    ::MessageBox( 0, "CreateProcess succeeded.", "TEST", MB_OK);
    if(ghJob)
    {
        if(0 == AssignProcessToJobObject( ghJob, processInfo.hProcess))
        {
            ::MessageBox( 0, "Could not AssignProcessToObject", "TEST", MB_OK);
        }
    }

    // Can we free handles now? Not sure about this.
    //CloseHandle(processInfo.hProcess); 
    CloseHandle(processInfo.hThread);
}

VISTA NOTE: See AssignProcessToJobObject always return "access denied" on Vista if you encounter access-denied issues with AssignProcessToObject() on vista.

KindDragon
  • 6,558
  • 4
  • 47
  • 75
jm.
  • 23,422
  • 22
  • 79
  • 93
  • To answer your question in the comment: Yes, you should CloseHandle when you don't need the handle anymore. – Adam Mitz Sep 12 '08 at 01:30
  • Yeah, but will that end the job? – jm. Sep 12 '08 at 07:27
  • 1
    No, I don't think so. JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE causes the job to terminate when the last *job* handle is closed, process handles shouldn't make a difference. Have you tried it? – Adam Mitz Sep 16 '08 at 02:13
  • 1
    It doesn't work on Windows 2000 :( (But it should under other versions of windows) – rook Jul 08 '10 at 20:56
  • Hi All, I tried this code in scope of one of my projects. My main app running 1 exe file which running 2 more. When I'm closing my main app sometimes other 3 are closed but sometimes no. Can anybody help to understand what's going on and how to fix this problem. – Davit Siradeghyan Aug 05 '11 at 12:12
  • See [this post](http://stackoverflow.com/questions/89588/assignprocesstojobobject-fails-with-access-denied-error-when-running-under-the) for the Vista+ `ACCESS_DENIED` when calling `AssignProcessToObject` problem (subordinate process already assigned to another job.) – vladr Jan 04 '13 at 19:33
  • 4
    There's a theoretical race condition here, if the new process exits quickly enough. To eliminate it, use the CREATE_SUSPENDED flag and call ResumeThread only after adding the process to the job. – Harry Johnston Jul 20 '16 at 01:51
  • 1
    I used JobObjects per described above in an executable, which calls MsBuild and other stuff. I can kill the exe at any time, and everything cleans up immediately. Super. :) – Andreas Vergison Jun 14 '17 at 17:40
  • ... In addition, I added Harry's idea to create processes in suspended mode, and resume them only after having added them to the JobObject. It may even be necessary (at least safer) to do it. I used NtResumeProcess rather than ResumeThread - blazing fast. See https://github.com/mridgers/clink/issues/420 . – Andreas Vergison Jun 14 '17 at 17:43
  • @AdamMitz it should work provided the only handle to the job object is hold by the parent process AND the operating system closes all process handles on its exit. Not sure if it is robust to assume the handle is closed immediately – dkrikun Jan 22 '18 at 18:33
5

One somewhat hackish solution would be for the parent process to attach to each child as a debugger (use DebugActiveProcess). When a debugger terminates all its debuggee processes are terminated as well.

A better solution (assuming you wrote the child processes as well) would be to have the child processes monitor the parent and exit if it goes away.

Rob Walker
  • 46,588
  • 15
  • 99
  • 136
  • 2
    Instead of having to call DebugActiveProcess, you could just pass DEBUG_PROCESS as one of your creation flags to CreateProcess. Less code that way. – mrduclaw Jul 17 '09 at 22:12
3

Windows Job Objects sounds like a good place to start. The name of the Job Object would have to be well-known, or passed to the children (or inherit the handle). The children would need to be notice when the parent dies, either through a failed IPC "heartbeat" or just WFMO/WFSO on the parent's process handle. At that point any child process could TermianteJobObject to bring down the whole group.

Adam Mitz
  • 6,025
  • 1
  • 29
  • 28
  • Instead a child process just can WFMO/WFSO on parent handle and close itself. Provided each child process does this there is no need for job object. – dkrikun Jan 22 '18 at 18:29
1

You can keep a separate watchdog process running. Its only task is watching the current process space to spot situations like you describe. It could even re-launch the original application after a crash or provide different options to the user, collect debug information, etc. Just try to keep it simple enough so that you don't need a second watchdog to watch the first one.

Pedro
  • 623
  • 1
  • 7
  • 12
0

You can assign a job to the parent process before creating processes:

static HANDLE hjob_kill_on_job_close=INVALID_HANDLE_VALUE;
void init(){
    hjob_kill_on_job_close = CreateJobObject(NULL, NULL);
    if (hjob_kill_on_job_close){
        JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobli = { 0 };
        jobli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        SetInformationJobObject(hjob_kill_on_job_close,
            JobObjectExtendedLimitInformation,
            &jobli, sizeof(jobli));
        AssignProcessToJobObject(hjob_kill_on_job_close, GetCurrentProcess());
    }
}
void deinit(){
    if (hjob_kill_on_job_close) {
        CloseHandle(hjob_kill_on_job_close);
    }
}

JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE causes all processes associated with the job to terminate when the last handle to the job is closed. By default, all child processes will be assigned to the job automatically, unless you passed CREATE_BREAKAWAY_FROM_JOB when calling CreateProcess. See https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags for more information about CREATE_BREAKAWAY_FROM_JOB.

You can use process explorer from Sysinternals to make sure all processes are assigned to the job. Just like this: enter image description here

Kohill Yang
  • 111
  • 1
  • 3
  • Please note that this method can only be used in Windows 8/10,since a process can be associated with only one job. Jobs cannot be nested. The ability to nest jobs was added in Windows 8 and Windows Server 2012. – Kohill Yang Sep 19 '21 at 13:18
-2

You'd probably have to keep a list of the processes you start, and kill them off one by one when you exit your program. I'm not sure of the specifics of doing this in C++ but it shouldn't be hard. The difficult part would probably be ensuring that child processes are shutdown in the case of an application crash. .Net has the ability to add a function that get's called when an unhandled exception occurs. I'm not sure if C++ offers the same capabilities.

Kibbee
  • 65,369
  • 27
  • 142
  • 182
-2

You could encapsulate each process in a C++ object and keep a list of them in global scope. The destructors can shut down each process. That will work fine if the program exits normally but it it crashes, all bets are off.

Here is a rough example:

class myprocess
{
public:
    myprocess(HANDLE hProcess)
        : _hProcess(hProcess)
    { }

    ~myprocess()
    {
        TerminateProcess(_hProcess, 0);
    }

private:
    HANDLE _hProcess;
};

std::list<myprocess> allprocesses;

Then whenever you launch one, call allprocessess.push_back(hProcess);

Adam Pierce
  • 33,531
  • 22
  • 69
  • 89