/*1、如果进程已经与一个作业相关联,就无法将当前进程及其任何子进程从作业中移除,这个安全特性可以保证 /* 进程无法摆脱对它施加的限制。 /*2、在调试程序时,调试器是从资源管理器启动的,程序会从调试器继承带“PCA”前缀的作业。因此,调试程序 /* 时总是显示进程已经加入了作业。使用命令行来运行程序时就不会有这个问题了。 /*3、关闭一个作业对象,并不会终止作业内所有的进程。作业对象实际上只是加了一个删除标记,只有在作业中 /* 所有进程都终止后,才会自动销毁。 /*4、可以向作业引用以下几种限制:(SetInformationJobObject函数来实现) 4.1 基本限制和额外限制,用于防止作业中的进程独占系统资源; 4.2 基本的UI限制,用于防止作业内的进程更改用户界面; 4.3 安全限额,用于防止作业内的进程访问安全资源(文件、注册表子项等) /*5、一旦进程已经加入一个作业,就不能再将其加入其他作业,该进程说创建的子进程也将处于其作业的限制内。 /* 可以给作业加上JOB_OBJECT_LIMIT_BREAKAWAY_OK限制,并在创建子进程时加上CREATE_BREAKAWAY_FROM_JOB标 /* 识,这样子进程就不会受到父进程作业的限制了。 /*6、终止作业中所有进程的方法, TerminateJobObject( __in HANDLE hJob, __in UINT uExitCode )函数终止作业。 */ #include "stdafx.h" #include <Windows.h> #include <iostream> using std::cout; using std::endl; BOOL AddThreadToJob(); int _tmain(int argc, _TCHAR* argv[]) { AddThreadToJob(); return 0; } BOOL AddThreadToJob() { BOOL bResult = FALSE; HANDLE hJob = NULL; try { BOOL bInJob = FALSE; //DWORD dwPid = GetCurrentProcessId(); //这里是获取进程的真实句柄 //HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); //GetCurrentProcess()返回的是伪句柄0xffffffff,但是本身是没有错误的 BOOL bRet = IsProcessInJob(GetCurrentProcess(), NULL, &bInJob); //CloseHandle(hProcess); if ( bInJob ) { JOBOBJECT_BASIC_PROCESS_ID_LIST jbril; memset(&jbril, 0, sizeof(jbril)); DWORD dwLen = 0; bRet = QueryInformationJobObject(NULL, JobObjectBasicProcessIdList, (LPVOID)&jbril, \ sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST), &dwLen); throw L"当前进程已经加入到作业中!"; } hJob = CreateJobObject(NULL, L"TestJob"); if ( NULL == hJob ) { throw L"创建作业失败!"; } if ( GetLastError() == ERROR_ALREADY_EXISTS ) { throw L"同名作业已经创建!"; } cout<<"给作业添加限制"<<endl; //设置作业基本限制 JOBOBJECT_BASIC_LIMIT_INFORMATION jbli = {0}; jbli.PriorityClass = IDLE_PRIORITY_CLASS; //对于进程占用时间超过分配时间的任何进程,系统将自动终止其运行 jbli.PerJobUserTimeLimit.QuadPart = 10000; jbli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS|JOB_OBJECT_LIMIT_JOB_TIME; SetInformationJobObject(hJob, JobObjectBasicLimitInformation, (LPVOID)&jbli, sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION)); //设置作业UI限制 cout<<"设置作业UI限制"<<endl; JOBOBJECT_BASIC_UI_RESTRICTIONS jbur; jbur.UIRestrictionsClass = JOB_OBJECT_UILIMIT_EXITWINDOWS; //不能关闭计算机 jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; //不能访问系统里的其他对象句柄 SetInformationJobObject(hJob, JobObjectBasicUIRestrictions, (LPVOID)&jbur, sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS)); //首先必须创建进程,然后挂起这个进程使他不能执行任何代码,然后才能加入作业中(确保进程在进入作业前不自信任何代码) //不然加入作业限制这个进程也就没意义了 STARTUPINFO si; memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(PROCESS_INFORMATION)); cout<<"创建一个新的进程"<<endl; bRet = CreateProcess(L"update.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED|CREATE_NEW_CONSOLE, NULL, \ NULL, &si, &pi); if ( !bRet ) throw L"创建进程失败!"; cout<<"子进程创建完毕"<<endl; //把这个进程加入到作业中去 AssignProcessToJobObject(hJob, pi.hProcess); //现在可以恢复这个进程,让他开始执行代码了 ResumeThread(pi.hThread); CloseHandle(pi.hThread); //等待进程终止,或者作业所申请的CPU时间都用完 HANDLE h[2]; h[0] = pi.hProcess; h[1] = hJob; cout<<"开始等待进程执行完毕或者作业CPU使用完"<<endl; DWORD dwRet = WaitForMultipleObjects(2, h, FALSE, INFINITE); switch( dwRet-WAIT_OBJECT_0 ) { case 0://进程终止 { int a = 0; cout<<"进程终止"<<endl;; break; } case 1://作业的CPU时间用完 { int a = 0; cout<<"作业的CPU时间用完"<<endl; break; } } CloseHandle(pi.hProcess); //获取进程执行时间信息 FILETIME createTime, exitTime, kernelTime, usedTime; GetProcessTimes(pi.hProcess, &createTime, &exitTime, &kernelTime, &usedTime); WCHAR szBuffer[256] = {0}; swprintf_s(szBuffer, 256, L"createTime=%u,exitTime=%u,kernelTime=%u,usedTime=%u", \ createTime.dwLowDateTime/1000, exitTime.dwLowDateTime/10000, \ kernelTime.dwLowDateTime/10000, usedTime.dwLowDateTime/10000); MessageBox(NULL, szBuffer, L"结束:", MB_OK); bResult = TRUE; } catch(WCHAR* pMsg) { MessageBox(NULL, pMsg, NULL, 0); } if ( hJob ) CloseHandle(hJob); return bResult; } 最后,运行该实例时需要从命令行来,因为资源管理器启动会默认加入系统作业。(上文有说明)