C++ WINAPI: How to kill child processes when the calling (parent) process is forcefully terminated

cwinapi

Could anyone kindly tell me how to kill child processes when the calling (parent) process is forcefully terminated? By the way, I am not able to change the source code of child application.

I have checked the existing thread in StackOverflow and it seems JobObject is the correct way to do so. However when I tested it (using a console application to call notepad.exe), I found that when the console application exited, Notepad didn't.

I used CreateProcess to spawn the new process.

I have also seen somebody says that setting up a pipe between parent process and child process will do the job, but I have not yet tried it.

If anyone can give me some hints, I would really appreciate it.

Update: WINAPI AssignProcessToJobObject failed to work if without | CREATE_BREAKAWAY_FROM_JOB in CreatProcess. Now it works!

Thank everyone.

Update again:

It's really tricky. I am always confused by Windows API.

Group 1:

Process flag: CREATE_SUSPENDED

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_SECURITY_RESTRICTED_TOKEN or JOB_OBJECT_SECURITY_NO_ADMIN or JOB_OBJECT_LIMIT_BREAKAWAY_OK

Result: AssingProcessToJobObject fails with Error code 5 Access is denied

Group 2:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_SECURITY_RESTRICTED_TOKEN or JOB_OBJECT_SECURITY_NO_ADMIN

Results: AssingProcessToJobObject succeeds, but child process fails to be killed when parent one is killed.

Group 3:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE

Results: AssingProcessToJobObject succeeds, and child process is automatically killed when parent one is killed.

Group 4:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK

Results: same to Group 3.

The following code uses JobObeject which I copied from http://cboard.cprogramming.com/windows-programming/60561-program-termination.html#post430075

#define _WIN32_WINNT 0x0500
#include <windows.h>

int main(void)
{
    HANDLE                               hJob;
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
    PROCESS_INFORMATION                  pi   = { 0 };
    STARTUPINFO                          si   = { 0 };


    /*
     * Create a job object.
     */
    hJob = CreateJobObject(NULL, NULL);

    /*
     * Causes all processes associated with the job to terminate when the
     * last handle to the job is closed.
     */
    jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));

    /*
     * Create the process suspended.
     */
    si.cb = sizeof(si);
    CreateProcess(TEXT("C:\\Windows\\System32\\Notepad.exe"), NULL, NULL, NULL, FALSE, 
                  CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB /*Important*/, NULL, NULL, &si, &pi);

    /*
     * Add the process to our job object.
     */
    AssignProcessToJobObject(hJob, pi.hProcess); // Does not work if without CREATE_BREAKAWAY_FROM_JOB


    /*
     * Start our suspended process.
     */
    ResumeThread(pi.hThread);

    /*
     * At this point, if we are closed, windows will automatically clean up
     * by closing any handles we have open. When the handle to the job object
     * is closed, any processes belonging to the job will be terminated.
     * Note: Grandchild processes automatically become part of the job and
     * will also be terminated. This behaviour can be avoided by using the
     * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK limit flag.
     */

    /*
     * Do what you like here. For demonstration purposes we will just wait
     * for the child process to complete. Click our close button to see
     * everything in action.
     */
    WaitForSingleObject(pi.hProcess, 3000);
    /*
     * Cleanup. As mentioned, Windows does this automagically when our process
     * exits, but it is good style to do it explicitly. 
     */
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    CloseHandle(hJob);

    return 0;
}

Best Answer

Do NOT test from within Visual Studio, via F5 or Ctrl+F5. When Visual Studio launch your program, it itself uses Job to manage things, and that interacts badly with your code.

Open a console and launch your exe "manually". Your code is correct ("works here", with VS2010 on Seven)

Edit: you may add error checking, don't assume all APIs always succeed.

Edit: you may use the IsProcessInJob API to know whether your process is already in a job when starting. If this is the case, child processes, by default, are created in that preexisting job, and you then have to use CREATE_BREAKAWAY_FROM_JOB (if you don't, you can't use AssignProcessToJobObject)

When starting the process from Visual Studio, or by double click in the Explorer,the process is launched in a job.

Adding, my code based on yours, works from VS or Explorer.

#include <Windows.h>
#include <stdio.h>

int main( void ) {

    BOOL bIsProcessInJob;
    BOOL bSuccess = IsProcessInJob( GetCurrentProcess(), NULL, &bIsProcessInJob );
    if ( bSuccess == 0 ) {
        printf( "IsProcessInJob failed: error %d\n", GetLastError() );
        return 0;
    }
    if ( bIsProcessInJob ) {
        MessageBox( NULL, L"Process is already in Job", L"Job Test", 0 );
    }

    HANDLE hJob = CreateJobObject( NULL, NULL );
    if ( hJob == NULL ) {
        printf( "CreateJobObject failed: error %d\n", GetLastError() );
        return 0;
    }

    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
    jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    bSuccess = SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jeli, sizeof( jeli ) );
    if ( bSuccess == 0 ) {
        printf( "SetInformationJobObject failed: error %d\n", GetLastError() );
        return 0;
    }

    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFO si = { 0 };
    si.cb = sizeof( si );
    DWORD dwCreationFlags = bIsProcessInJob ? CREATE_BREAKAWAY_FROM_JOB : 0;
    bSuccess = CreateProcess( L"C:\\Windows\\System32\\Notepad.exe", NULL, NULL, NULL, FALSE, 
                              dwCreationFlags, NULL, NULL, &si, &pi);
    if ( bSuccess == 0 ) {
        printf( "CreateProcess failed: error %d\n", GetLastError() );
        return 0;
    }

    bSuccess = AssignProcessToJobObject( hJob, pi.hProcess );
    if ( bSuccess == 0 ) {
        printf( "AssignProcessToJobObject failed: error %d\n", GetLastError() );
        return 0;
    }

    CloseHandle( pi.hThread );
    CloseHandle( pi.hProcess );

    printf( "Type a key to exit..." );
    getchar();

    CloseHandle( hJob );

    return 0;

}
Related Topic