TerminateProcess在Windows 10上没有超过

时间:2016-05-03 23:47:10

标签: c++ windows process

我们的C ++应用程序使用CreateProcess启动单独的.exe(可以启动自己的子进程),如下所示。

BOOL started = ::CreateProcess(NULL,      // application
                p,         // parameters
                NULL,        // process security
                NULL,        // thread security
                TRUE,        // inherit handles flag
                0,           // flags
                NULL,        // inherit environment
                dirLP,      // inherit directory
                &startup,    // STARTUPINFO
                &procinfo);  // PROCESS_INFORMATIO

如果我们需要取消“作业”,我们使用CreateToolhelp32Snapshot迭代进程列表以查找我们启动的子进程。

static BOOL TerminateProcessTree (HANDLE parentProcess,UINT exitCode) 
{ 
    BOOL            result=TRUE;
    HANDLE         hProcessSnap = NULL; 
    PROCESSENTRY32 pe32      = {0}; 
    //  Take a snapshot of all processes in the system. 

    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

    if (hProcessSnap == INVALID_HANDLE_VALUE) 
        return (FALSE); 
     pe32.dwSize = sizeof(PROCESSENTRY32); 

    //  Walk the snapshot of the processes
    DWORD parentID=GetProcessId(parentProcess);
    if(parentID==0){
        PrintLastError("GetProcessId");
        return FALSE;
    }
   if (Process32First(hProcessSnap, &pe32)) { 
        do{ 
            if(pe32.th32ParentProcessID==parentID){

               HANDLE hProcess = OpenProcess (PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID); 
               if(hProcess!=NULL){
                    BOOL terminateChildren=TerminateProcessTree(hProcess,exitCode);
                    BOOL terminatedChild = TerminateProcess(hProcess, exitCode);
                    if (!terminatedChild){
                        PrintLastError("TerminateProcess");
                    }
                    CloseHandle(hProcess);
                    if(!terminatedChild || !terminateChildren){
                        result=FALSE;
                        break;
                    }
               } else{
                   PrintLastError("OpenProcess");
               }
            }

        }while (Process32Next(hProcessSnap, &pe32)); 
    }
    CloseHandle (hProcessSnap); 
    DWORD checkCode=0;
    BOOL terminated;
    if(GetExitCodeProcess(parentProcess,&checkCode) && checkCode==STILL_ACTIVE){
        terminated=TerminateProcess(parentProcess,exitCode);
        if (!terminated){
            PrintLastError("TerminateProcess");
            result= FALSE;
        }
    }
    return result;
}

如上所述,这在Windows 7上运行正常.Windows 10在第一次调用“TerminateProcess”时失败并显示“Access Denied”。显然,当涉及到流程时,Windows安全模型中发生了一些变化。

1 个答案:

答案 0 :(得分:0)

处理控制对象进程树的强大方法是使用注释线程中所述的Job对象。

要记住的一件事是,一个进程只能属于一个作业,默认情况下,操作系统确定需要appcompat帮助的任何EXE都被放入一个由"程序兼容性自动管理的作业对象中助理&#34 ;.这使得使用Job对象来管理任意第三方EXE更复杂(即AssignProcessToJobObject对这些进程失败。)

如果您使用CreateProcess,则可以使用标记CREATE_BREAKAWAY_FROM_JOB。见this blog post。只要目标EXE具有与调用对象相同的权限,这就可以正常工作。

对于标准用户EXE运行可能需要管理员权限的EXE(即它们包含标记为requireAdministrator的清单),您必须使用ShellExecuteShellExecuteEx作为在这种情况下调用CreateProcess将失败。如果您的目标EXE都使用了正确的manifest elements,那么它就不会被放入PCA作业对象中。您可以使用传递SEE_MASK_FLAG_NO_UI的技巧,这会产生副作用避免PCA的工作行为。如果您要启动任意第三方EXE,则应使用ShellExecuteEx而不是CreateProcess

bool SpawnProcess( const WCHAR* szExePath, const WCHAR* szExeArgs )
{
    if( !szExePath )
        return false;

    // NOTE: szExeArgs can be nullptr.

    // Get working directory from executable path.    
    WCHAR szDirectory[MAX_PATH] = {0};
    wcscpy_s( szDirectory, szExePath );
    PathRemoveFileSpec( szDirectory );

    // ShellExecute or ShellExecuteEx must be used instead of CreateProcess
    // to permit the shell to display a UAC prompt asking for consent to
    // elevate when the target executable's manifest specifies a run level
    // of "requireAdministrator".
    //
    // You can only use CreateProcess if you know that the application you
    // are spawning will be at the same run level as the current process.
    // Otherwise, you will receive ERROR_ACCESS_DENIED if the elevation
    // consent could not be obtained.
    SHELLEXECUTEINFO info = {};
    info.cbSize = sizeof( info );
    info.lpVerb = L"open";
    info.fMask = SEE_MASK_FLAG_NO_UI;
    info.lpFile = szExePath;
    info.lpParameters = szExeArgs;
    info.lpDirectory = szDirectory;
    info.nShow = SW_SHOW;
    if( !ShellExecuteEx( &info ) )
        return false;

    return true;
}

请注意,如果您想等到此流程退出,您可以使用:

bool SpawnProcessAndWait( const WCHAR *szExePath, const WCHAR *szExeArgs, DWORD *pdwExitCode )
{
    if( !szExePath )
        return false;

    // NOTE: szExeArgs and pExitCode can be nullptr.

    // Get working directory from executable path.    
    WCHAR szDirectory[MAX_PATH] = {0};
    wcscpy_s( szDirectory, szExePath );
    PathRemoveFileSpec( szDirectory );

    // See SpawnProcess for information why ShellExecute or ShellExecuteEx
    // must be used instead of CreateProcess.
    SHELLEXECUTEINFO info = {};
    info.cbSize = sizeof( info );
    info.lpVerb = L"open";
    info.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
    info.lpFile = szExePath;
    info.lpParameters = szExeArgs;
    info.lpDirectory = szDirectory;
    info.nShow = SW_SHOW;
    if( !ShellExecuteEx( &info ) )
        return false;

    // Wait for process to finish.
    WaitForSingleObject( info.hProcess, INFINITE );

    // Return exit code from process, if requested by caller.
    if( pdwExitCode )
        GetExitCodeProcess( info.hProcess, pdwExitCode );

    CloseHandle( info.hProcess );
    return true;
}

你还没有注意到你的"主人"此处的应用程序使用管理员或标准用户权限。理想情况下,它遵循可追溯到Windows Vista的User Account Control指南使用标准用户权限。如果您的应用以标准用户身份运行,那么由于安全性方面的改进,您的代码可能不再有效,以防止非管理员恶意软件对其他进程执行此类操作。

  

有关Windows 10的一般appcompat问题,请务必阅读Windows and Windows Server compatibility cookbook,如果您直接从Windows 7跳转到Windows 10,请回顾Windows 8.0和Windows 8.1材料。