简体   繁体   English

C ++ WINAPI:如何在强制终止调用(父)进程时终止子进程?

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

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. 我已经检查了StackOverflow中的现有线程,看起来JobObject是正确的方法。 However when I tested it (using a console application to call notepad.exe), I found that when the console application exited, Notepad didn't. 但是,当我测试它(使用控制台应用程序来调用notepad.exe)时,我发现当控制台应用程序退出时,记事本没有。

I used CreateProcess to spawn the new process. 我使用CreateProcess来生成新进程。

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 更新:如果没有| CREATE_BREAKAWAY_FROM_JOB则WINAPI AssignProcessToJobObject无法正常工作 | CREATE_BREAKAWAY_FROM_JOB in CreatProcess . | CREATE_BREAKAWAY_FROM_JOB中的CreatProcess Now it works! 现在它有效!

Thank everyone. 谢谢大家。

Update again: 再次更新:

It's really tricky. 这真的很棘手。 I am always confused by Windows API. 我总是对Windows API感到困惑。

Group 1: 第1组:

Process flag: CREATE_SUSPENDED 进程标志: 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 JobObject标志: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_SECURITY_RESTRICTED_TOKEN or JOB_OBJECT_SECURITY_NO_ADMIN or JOB_OBJECT_LIMIT_BREAKAWAY_OK 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 结果: AssingProcessToJobObject失败,错误代码为5拒绝访问

Group 2: 第2组:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB 进程标志: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB 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 JobObject标志: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_SECURITY_RESTRICTED_TOKEN or JOB_OBJECT_SECURITY_NO_ADMIN 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. 结果: AssingProcessToJobObject成功,但是当父项被杀死时,子进程无法被杀死。

Group 3: 第3组:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB 进程标志: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE JobObject标志: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE

Results: AssingProcessToJobObject succeeds, and child process is automatically killed when parent one is killed. 结果: AssingProcessToJobObject成功,子进程在父进程被AssingProcessToJobObject时自动AssingProcessToJobObject

Group 4: 第4组:

Process flag: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB 进程标志: CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject flag: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK JobObject标志: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK

Results: same to Group 3. 结果:与第3组相同。

The following code uses JobObeject which I copied from http://cboard.cprogramming.com/windows-programming/60561-program-termination.html#post430075 以下代码使用我从http://cboard.cprogramming.com/windows-programming/60561-program-termination.html#post430075复制的JobObeject

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

Do NOT test from within Visual Studio, via F5 or Ctrl+F5. 不要在Visual Studio中通过F5或Ctrl + F5进行测试。 When Visual Studio launch your program, it itself uses Job to manage things, and that interacts badly with your code. 当Visual Studio启动您的程序时,它本身使用Job来管理事物,并且与您的代码进行严重交互。

Open a console and launch your exe "manually". 打开控制台并“手动”启动exe。 Your code is correct ("works here", with VS2010 on Seven) 你的代码是正确的(“在这里工作”,VS2010在七上)

Edit: you may add error checking, don't assume all APIs always succeed. 编辑:您可以添加错误检查,不要假设所有API始终成功。

Edit: you may use the IsProcessInJob API to know whether your process is already in a job when starting. 编辑:您可以使用IsProcessInJob API来了解您的进程在启动时是否已在作业中。 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 ) 如果是这种情况,默认情况下,子进程是在该先前存在的作业中创建的,然后您必须使用CREATE_BREAKAWAY_FROM_JOB (如果不这样做,则不能使用AssignProcessToJobObject

When starting the process from Visual Studio, or by double click in the Explorer,the process is launched in a job. 从Visual Studio启动进程时,或者在资源管理器中双击时,将在作业中启动该进程。

Adding, my code based on yours, works from VS or Explorer. 添加,我的代码基于您的代码,可以在VS或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;

}

To summerize my comments: 总结一下我的意见:

The process that spawns the child have to create the job and is the only that has an open handle to it. 生成孩子的过程必须创建作业,并且是唯一具有开放处理的作业。 All spwaned child processes are then part of the job. 然后,所有spwaned子进程都是工作的一部分。

hJob = CreateJobObject(NULL, NULL);

JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {
    0, 0, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, 0
};
SetInformationJobObject(hJob, &jeli);

if (AssignProcessToJobObject(hJob, GetCurrentProcess()) == FALSE) {
    DWORD Error = GetLastError();
}

If the above assignment fails, try to play with security flags like JOB_OBJECT_SECURITY_NO_ADMIN or JOB_OBJECT_SECURITY_RESTRICTED_TOKEN . 如果上述分配失败,请尝试使用JOB_OBJECT_SECURITY_NO_ADMINJOB_OBJECT_SECURITY_RESTRICTED_TOKEN等安全标志。

MSDN about AssignProcessToJobObject : 有关AssignProcessToJobObject MSDN:

If the process is already running and the job has security limitations, AssignProcessToJobObject may fail. 如果进程已在运行且作业存在安全限制,则AssignProcessToJobObject可能会失败。 For example, if the primary token of the process contains the local administrators group, but the job object has the security limitation JOB_OBJECT_SECURITY_NO_ADMIN, the function fails. 例如,如果进程的主令牌包含本地管理员组,但作业对象具有安全限制JOB_OBJECT_SECURITY_NO_ADMIN,则该函数将失败。 If the job has the security limitation JOB_OBJECT_SECURITY_ONLY_TOKEN, the process must be created suspended. 如果作业具有安全限制JOB_OBJECT_SECURITY_ONLY_TOKEN,则必须创建挂起的进程。 To create a suspended process, call the CreateProcess function with the CREATE_SUSPENDED flag. 要创建挂起的进程,请使用CREATE_SUSPENDED标志调用CreateProcess函数。

A process can be associated only with a single job. 进程只能与单个作业相关联。 A process inherits limits from the job it is associated with and adds its accounting information to the job. 进程从与其关联的作业继承限制,并将其记帐信息添加到作业。 If a process is associated with a job, all processes it creates are associated with that job by default. 如果进程与作业关联 ,则默认情况下, 它创建的所有进程都与该作业相关联 To create a process that is not part of the same job, call the CreateProcess function with the CREATE_BREAKAWAY_FROM_JOB flag. 要创建不属于同一作业的进程,请使用CREATE_BREAKAWAY_FROM_JOB标志调用CreateProcess函数。

So child processes are automatically part of the job in this case. 因此,在这种情况下,子进程自动成为作业的一部分。 However in order to create a job, it could be necessary to start the console application with higher rights. 但是,为了创建作业,可能需要以更高的权限启动控制台应用程序。 What is the error code returned after call to AssignProcessToJobObject(hJob, GetCurrentProcess()) ? 调用AssignProcessToJobObject(hJob, GetCurrentProcess())后返回的错误代码是什么?

MSDN about CREATE_BREAKAWAY_FROM_JOB : 有关CREATE_BREAKAWAY_FROM_JOB MSDN:

The child processes of a process associated with a job are not associated with the job. 与作业关联的进程的子进程与作业无关。

If the calling process is not associated with a job, this constant has no effect. 如果调用进程未与作业关联,则此常量无效。 If the calling process is associated with a job, the job must set the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit. 如果调用进程与作业关联,则作业必须设置JOB_OBJECT_LIMIT_BREAKAWAY_OK限制。

The flag CREATE_BREAKAWAY_FROM_JOB should prevent that the child process is part of the job. 标志CREATE_BREAKAWAY_FROM_JOB应该防止子进程是作业的一部分。 But this only applies if JOB_OBJECT_LIMIT_BREAKAWAY_OK was setfor the job. 但是,这仅适用于如果JOB_OBJECT_LIMIT_BREAKAWAY_OK是setfor工作。

Just a thought, if the intention is just to kill child processes when Parent Process is killed: 只是一个想法,如果意图只是在父进程被杀死时杀死子进程:

What if keep the child process handles and terminate them during the OnClose(or whenever needed) of the parent Process. 如果保留子进程句柄并在父进程的OnClose(或需要时)终止它们,该怎么办? Something like this: 像这样的东西:

 // Creating
 UINT u = 0;
 HANDLE* pH = new HANDLE[2];
 for (int i =0 ; i< 2; i++)
 {
    STARTUPINFO si ;
    ZeroMemory(&si, sizeof(si));
    si.wShowWindow = SW_SHOW;
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

    if (!CreateProcess(TEXT("C:\\Windows\\System32\\Notepad.exe"), NULL, NULL, NULL, FALSE, 
                   NULL, NULL, NULL, &si, &pi))
                   OutputDebugString(_T("Failed"));
    else
    OutputDebugString(_T("Success"));

    pH[i] = pi.hProcess;
  }

// Killing children
i = 0;
while (i<2)
{
::TerminateProcess(pH[i], u);
i++;
}

Rather than terminate process, send message could also be after enumerating the window handles. 而不是终止进程,发送消息也可以在枚举窗口句柄之后。 Let me know if I am anything is missing. 如果我遗失了,请告诉我。

I have a somewhat similar situation. 我的情况有点类似。 My main application creates a child process that is used to log "events". 我的主应用程序创建一个用于记录“事件”的子进程。 The child process keeps a record of its parent processes (it can have many). 子进程保留其父进程的记录(它可以有很多)。 Using a timer in the child process, I am able to check if the parent process has crashed, or, shut down. 在子进程中使用计时器,我能够检查父进程是否已崩溃,或者是否已关闭。 When this is detected, the child process shuts down cleanly. 检测到此情况后,子进程会干净地关闭。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM