我们的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安全模型中发生了一些变化。
答案 0 :(得分:0)
处理控制对象进程树的强大方法是使用注释线程中所述的Job对象。
要记住的一件事是,一个进程只能属于一个作业,默认情况下,操作系统确定需要appcompat帮助的任何EXE都被放入一个由"程序兼容性自动管理的作业对象中助理&#34 ;.这使得使用Job对象来管理任意第三方EXE更复杂(即AssignProcessToJobObject
对这些进程失败。)
如果您使用CreateProcess
,则可以使用标记CREATE_BREAKAWAY_FROM_JOB
。见this blog post。只要目标EXE具有与调用对象相同的权限,这就可以正常工作。
对于标准用户EXE运行可能需要管理员权限的EXE(即它们包含标记为requireAdministrator
的清单),您必须使用ShellExecute
或ShellExecuteEx
作为在这种情况下调用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材料。