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

c++, winapi

Solution

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;

}

Problem

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; } ```

Original source