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

时间:2014-06-03 10:26:23

标签: c++ winapi

有人可以告诉我如何强制终止调用(父)进程时杀死子进程吗?顺便说一句,我无法更改子应用程序的源代码。

我已经检查了StackOverflow中的现有线程,看起来JobObject是正确的方法。但是当我测试它时(使用控制台应用程序来调用notepad.exe),我发现当控制台应用程序退出时,记事本没有。

我使用CreateProcess来生成新进程。

我也看到有人说在父进程和子进程之间建立管道就可以了,但我还没有尝试过。

如果有人能给我一些提示,我会非常感激。

更新:如果AssignProcessToJobObject中没有| CREATE_BREAKAWAY_FROM_JOB,则WINAPI CreatProcess无效。现在它有效!

谢谢大家。

再次更新:

这真的很棘手。我总是对Windows API感到困惑。

第1组:

处理标记:CREATE_SUSPENDED

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

结果:AssingProcessToJobObject失败,错误代码为5拒绝访问

第2组:

处理标记:CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject标志:JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_SECURITY_RESTRICTED_TOKEN or JOB_OBJECT_SECURITY_NO_ADMIN

结果:AssingProcessToJobObject成功,但是当父项被杀死时,子进程无法被杀死。

第3组:

处理标记:CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject标志:JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE

结果:AssingProcessToJobObject成功,子进程在父进程被杀死时自动终止。

第4组:

处理标记:CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB

JobObject标志:JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK

结果:与第3组相同。

以下代码使用我从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;
}

4 个答案:

答案 0 :(得分:8)

不要在Visual Studio中通过F5或Ctrl + F5进行测试。当Visual Studio启动您的程序时,它本身使用Job来管理事物,并且与您的代码交互不良。

打开控制台并“手动”启动exe。你的代码是正确的(“在这里工作”,VS2010在七上)

编辑:您可以添加错误检查,不要假设所有API始终成功。

编辑:您可以使用IsProcessInJob API来了解您的流程在启动时是否已经在工作中。如果是这种情况,默认情况下,子进程是在该先前存在的作业中创建的,然后您必须使用CREATE_BREAKAWAY_FROM_JOB(如果不这样做,则不能使用AssignProcessToJobObject

从Visual Studio启动流程或在资源管理器中双击时,该流程将在作业中启动。

添加,我的代码基于你的代码,可以在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;

}

答案 1 :(得分:1)

总结我的评论:

生成孩子的过程必须创建作业,并且是唯一具有打开句柄的作业。然后,所有被淹没的子进程都是工作的一部分。

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();
}

如果上述分配失败,请尝试使用JOB_OBJECT_SECURITY_NO_ADMINJOB_OBJECT_SECURITY_RESTRICTED_TOKEN等安全标记。

关于AssignProcessToJobObject的MSDN:

  

如果进程已在运行且作业具有安全性   限制,AssignProcessToJobObject可能会失败。例如,如果   进程的主令牌包含本地管理员组,   但是作业对象具有安全限制   JOB_OBJECT_SECURITY_NO_ADMIN,函数失败。如果工作有   安全限制JOB_OBJECT_SECURITY_ONLY_TOKEN,进程必须   被创建暂停。要创建暂停的进程,请调用   带有CREATE_SUSPENDED标志的CreateProcess函数。

     

流程只能与单个作业相关联。进程继承   与其相关的工作限制并添加其会计   工作的信息。如果进程与作业相关联,则全部   默认情况下,它创建的进程与该作业关联。至   创建一个不属于同一工作的流程,调用   带有CREATE_BREAKAWAY_FROM_JOB标志的CreateProcess函数。

因此,在这种情况下,子进程自动成为作业的一部分。但是,为了创建作业,可能需要以更高的权限启动控制台应用程序。调用AssignProcessToJobObject(hJob, GetCurrentProcess())后返回的错误代码是什么?

关于CREATE_BREAKAWAY_FROM_JOB的MSDN:

  

与作业关联的进程的子进程不是   与工作相关联。

     

如果调用进程未与作业关联,则此常量具有   没有效果。如果调用进程与作业相关联,则该作业   必须设置JOB_OBJECT_LIMIT_BREAKAWAY_OK限制。

标志CREATE_BREAKAWAY_FROM_JOB应该防止子进程成为作业的一部分。但这仅适用于为作业设置JOB_OBJECT_LIMIT_BREAKAWAY_OK的情况。

答案 2 :(得分:0)

只是想一想,如果故意只是在杀死父进程时杀死子进程:

如果在父进程的OnClose(或任何需要)期间保留子进程句柄并终止它们,该怎么办?像这样:

 // 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++;
}

在枚举窗口句柄之后,也可以发送消息,而不是终止进程。如果我遗失了任何东西,请告诉我。

答案 3 :(得分:0)

我的情况有点类似。我的主应用程序创建一个用于记录“事件”的子进程。子进程保留其父进程的记录(它可以有很多)。在子进程中使用计时器,我能够检查父进程是否已崩溃,或者是否已关闭。检测到此情况后,子进程将完全关闭。