5.6 作业对象事件和完成端口
(1)将作业对象与完成端口对象关联起来
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;
joacp.CompletionKey = hJob1; //可用来标识作业对象任意唯一值,这里取其句柄
joacp.CompletionPort = hIOCP; //完成端口的句柄
SetInformationJobObject(hJob,JobObjectAssociateCompletionPortInformation,
&joacp,sizeof(joacp));
(2)创建线程,将完成端口对象作为参数传入线程函数。并GetQueuedCompletionStatus来等待作业对象的通知事件。
参数 |
描述 |
hIOCP |
要获取事件的完成端口对象的句柄 |
pNumBytesTransferred |
等待的事件ID 【与作业对象有关的事件】 ①JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:作业对象中活动进程数达到上限时通知 ②JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:作业对象中当前没有活动进程时通知 ③JOB_OBJECT_MSG_END_OF_JOB_TIME:作业对象耗尽指定的时间片时通知。但其中的进程不会自动终止。可以设置一个新的时间限额以允许继续,或调用TerminateJobObject来终止进程。 ④JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:作业对象耗尽指定的内存时通知,同时给出进程ID 【与进程有关的事件】 ①JOB_OBJECT_MSG_NEW_PROCESS:新进程加入作业对象时通知,并给出进程ID。 ②JOB_OBJECT_MSG_EXIT_PROCESS:进程正常退出时通知,并给出进程ID。 ③JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:进程异常退出时通知,并给出进程ID ④JOB_OBJECT_MSG_END_OF_PROCESS_TIME:进程耗尽时间片时通知,进程将终止,并给出进程ID ⑤JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:进程消耗内存数量达到上限时通知,同时给出进程ID。 |
pCompletionKey |
指定触发这个事件的作业对象的句柄(可以将一个完成端口对象与多个作业对象进行绑定) |
pOverlapped |
在作业事件中,该值表示哪个进程ID发生的事件。 |
dwMilliseconds |
用于指定调用者等待完成端口的时间 |
注意:
①作业对象的状态变为己触发是在分配作业的时间到期时,而不是所有进程都结束时。
②默认下,当作业时间到期时,它的所有进程都会自动终止,所以也就不会投递JOB_OBJECT_MSG_END_OF_JOB_TIME。如果只想发送该通知给应用程序,而让应用程序自行来杀死进程,可以做如下设置:
//创建结构体,并将JOb结束时要采取的措施填入该结构体
JOBOBJECT_END_OF_JOB_TIME_INFORMATION joeojti;
joeojti.EndOfJobTimeAction = JOB_OBJECT_POST_AT_END_OF_JOB; //投递通知,而不是“杀死”进程。创建作业时,默认值为JOB_OBJECT_TERMINATE_AT_END_OF_JOB;
//告诉作业对象当作业时间到期时,要采取的措施
SetInformationJobObject(hJob,JobObjectEndOfJobTimeInformation,
&joeojti,sizeof(joeojti));
【JobIOCP】利用完成端口实现进程管理
#include <windows.h> #include <locale.h> #include <tchar.h> #include <strsafe.h> HANDLE g_hJob = NULL; HANDLE g_hIOCP = NULL; DWORD WINAPI IOCPThread(LPVOID lpParam); __inline VOID GetAppPath(LPTSTR pszBuffer) { DWORD dwLen = 0; //pszbuffer中接收到的字符个数 //获取当前应用程序全路径(含文件名) dwLen = GetModuleFileName(NULL, pszBuffer, MAX_PATH); if (0 == dwLen) return; //去件文件及扩展名,只保留路径(路径的最后面含"\"字符) for (DWORD i = dwLen; i > 0; i--){ if ('\\'==pszBuffer[i]){ pszBuffer[i + 1] = '\0'; break; } } } int _tmain() { _tsetlocale(LC_ALL, _T("chs")); //创建一个默认的安全属性结构(不继承) SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) }; sa.bInheritHandle = FALSE; //创建匿名的Job对象 g_hJob = CreateJobObject(&sa, NULL); //指定不显示异常关闭对话框,即静默方式运行本程序 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {}; //jeli.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = 100 * 10000i64; jeli.BasicLimitInformation.LimitFlags =JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; SetInformationJobObject(g_hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); //为作业对象设置一些限制(经测试,以下的基本限制要在扩展限制设置完以后或,才能设置//也可以直接在扩展限制里面直接设置!) JOBOBJECT_BASIC_LIMIT_INFORMATION jbli = {0}; //限制进程的用户时间最大值,单位是100纳秒,本例中设置为100ms jbli.PerProcessUserTimeLimit.QuadPart = 100 * 10000i64; //该进程能够获得的CPU执行时间最多为100ms jbli.PerJobUserTimeLimit.QuadPart = 350 * 10000i64; //整个作业的时间片为350ms(指CPU执行时间) //限制最大工作集为256K //jbli.MaximumWorkingSetSize = 256 * 1024; //jbli.MinimumWorkingSetSize = 4 * 1024; //这也是页面的大小(4K) jbli.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME | JOB_OBJECT_LIMIT_JOB_TIME; /*| JOB_OBJECT_LIMIT_WORKINGSET*/; SetInformationJobObject(g_hJob, JobObjectBasicLimitInformation, &jbli, sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION)); //创建完成端口对象 /* HANDLE CreateIoCompletionPort ( HANDLE FileHandle, // 有效的文件句柄或INVALID_HANDLE_VALUE HANDLE ExistingCompletionPort, // 已经存在的完成端口。如果为NULL,则为新建一个IOCP ULONG_PTR CompletionKey, // completion key是传送给处理函数的参数 DWORD NumberOfConcurrentThreads // number of threads to execute concurrently ); */ g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1); //1个线程的 //将Job对象与完成端口对象绑定 JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobiocp; jobiocp.CompletionKey = g_hJob; jobiocp.CompletionPort = g_hIOCP; SetInformationJobObject(g_hJob, JobObjectAssociateCompletionPortInformation, &jobiocp, sizeof(jobiocp)); //启动监视Job事件的IOCP线程(1个线程) HANDLE hIOCPThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)IOCPThread , (LPVOID)g_hIOCP, 0, NULL); TCHAR pAppPath[MAX_PATH] = {}; GetAppPath(pAppPath); StringCchCat(pAppPath, MAX_PATH, _T("ErrorShow.exe"));//用课本的ErrorShow程序演示 const int iProcessNums = 3; STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION piArray[iProcessNums] = { { 0 }, { 0 }, { 0 } }; HANDLE h[iProcessNums]; //以暂停主线程方式创建一个独立的进程(可以改为循环创建多个) //注意加上CREATE_BREAKAWAY_FROM_JOB标志 for (int i = 0; i < iProcessNums; i++) { CreateProcess(pAppPath, NULL, &sa, &sa, FALSE, CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &piArray[i]); //将进程与Job对象绑定 AssignProcessToJobObject(g_hJob, piArray[i].hProcess); ResumeThread(piArray[i].hThread); //恢复新进程的主线程 h[i] = piArray[i].hProcess; //查询一些作业对象的统计信息,本例中查询基本统计信息和IO统计信息 JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION jobioinfo; DWORD dwNeedLen = 0; QueryInformationJobObject(g_hJob, JobObjectBasicAndIoAccountingInformation, &jobioinfo, sizeof(jobioinfo), &dwNeedLen); } //等进程退出 WaitForMultipleObjects(iProcessNums, h, TRUE, INFINITE); for (int i = 0; i < iProcessNums;i++) { CloseHandle(piArray[i].hProcess); CloseHandle(piArray[i].hThread); } //向IOCPThread函数发送一个退出的消息,指定dwNumberOfBytesTransferred为0 //该值直接传递给GetQueuedCompletionStatus函数中对应的参数,用来表示事件ID PostQueuedCompletionStatus(g_hIOCP,0,(ULONG_PTR)g_hJob,NULL); //等待线程退出 WaitForSingleObject(hIOCPThread,INFINITE); CloseHandle(hIOCPThread); //关闭作业对象等 CloseHandle(g_hJob); CloseHandle(g_hIOCP); _tsystem(TEXT("PAUSE")); return 0; } DWORD WINAPI IOCPThread(LPVOID lpParam) { ULONG_PTR hJob = NULL; HANDLE hIocp = (HANDLE)lpParam; OVERLAPPED* lpOverlapped = NULL; BOOL bLoop = TRUE; DWORD dwReasonID = 0; //事件ID,参数lpNumberOfBytes DWORD dwProcessID = 0; while (bLoop) { if (!GetQueuedCompletionStatus(hIocp, &dwReasonID, (PULONG_PTR)&hJob, &lpOverlapped, INFINITE)) { _tprintf(_T("IOCPThread:GetQueueCompletionStatus调用失败,错误代码:0x%08x\n"), GetLastError()); continue; } switch (dwReasonID) { //作业对象中活动进程数达到上限 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: _tprintf(_T("作业对象中活动进程数己达到上限!\n")); break; //作业对象中当前没有活动进程 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: _tprintf(_T("作业对象中当前没有活动进程!\n")); break; //作业对象耗尽指定的时间片 case JOB_OBJECT_MSG_END_OF_JOB_TIME: _tprintf(_T("作业对象耗尽指定的时间片!\n")); break; //作业对象耗尽指定的内存 case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT: dwProcessID = (DWORD)lpOverlapped; _tprintf(_T("进程[ID:%u]导致作业对象消耗内存达到上限!\n"),dwProcessID); break; //新进程加入作业对象 case JOB_OBJECT_MSG_NEW_PROCESS: dwProcessID = (DWORD)lpOverlapped; _tprintf(_T("进程[ID:%u]加入作业对象[h:0x%08X]!\n"), dwProcessID,hJob); break; //进程正常退出 case JOB_OBJECT_MSG_EXIT_PROCESS: { dwProcessID = (DWORD)lpOverlapped; DWORD dwExitCode = 0; //注意进程退出了,但其内核对象并没释放,还可以从内核对象中获取退出码 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessID); if (NULL != hProcess) { GetExitCodeProcess(hProcess, &dwExitCode); CloseHandle(hProcess); } _tprintf(_T("进程[ID:%u]正常退出,退出码:%u!\n"), dwProcessID, dwExitCode); } break; //进程异常退出 case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: { dwProcessID = (DWORD)lpOverlapped; DWORD dwExitCode = 0; //注意进程退出了,但其内核对象并没释放,还可以从内核对象中获取退出码 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessID); if (NULL != hProcess) { GetExitCodeProcess(hProcess, &dwExitCode); CloseHandle(hProcess); } _tprintf(_T("进程[ID:%u]异常退出,退出码:%u!\n"), dwProcessID, dwExitCode); } break; //进程耗尽时间片 case JOB_OBJECT_MSG_END_OF_PROCESS_TIME: dwProcessID = (DWORD)lpOverlapped; _tprintf(_T("进程[ID:%u]耗尽时间片!\n"), dwProcessID); break; //进程消耗内存数量达到上限 case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: dwProcessID = (DWORD)lpOverlapped; _tprintf(_T("进程[ID:%u]消耗内存达到上限!\n"), dwProcessID); break; default: bLoop = FALSE; break; } } _tprintf(_T("ICOP线程(ID:0x%x退出)\n"), GetCurrentThreadId()); return 0; }
【JobLab程序】演示作业限制设置
/****************************************************************************** Module: JobLab.cpp Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre ******************************************************************************/ #include "..\\..\\CommonFiles\CmnHdr.h" #include "resource.h" #include "Job.h" #include <tchar.h> #include <strsafe.h> ////////////////////////////////////////////////////////////////////////// HWND g_hwnd; //对话框句柄(被所有线程访问) HANDLE g_hIOCP; //用来接收作业通知的完成端口 HANDLE g_hThreadIOCP;//完成端口线程句柄 CJob g_job; //作业对象句柄 //完成端口的CompletionKey #define COMPKEY_TERMINATE ((UINT_PTR)0) #define COMPKEY_STATUS ((UINT_PTR)1) #define COMPKEY_JOBOBJECT ((UINT_PTR)2) ////////////////////////////////////////////////////////////////////////// void GetProcessName(DWORD PID, PTSTR szProcessName, size_t cchSize){ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); if (hProcess == NULL){ _tcscpy_s(szProcessName, cchSize, TEXT("???")); return; } ////GetModuleFileNameEx函数须包含Psapi.h,返回值0表示调用失败 //if (GetModuleFileNameEx(hProcess,(HMODULE)0,szProcessName,cchSize)==0){ // //当进程加入作业对象时,其地址空间可能并没有完全初始化时, // //这里调用GetModuleFileNameEx可能会失败。 // // //调用失败,可能是文件名复杂的路径,诸如: // //\Device\HarddiskVolume1\Windows\System32\notepad.exe,可以调用 // //GetProcessImageFileName来获得这种路径 // if (!GetProcessImageFileName(hProcess, szProcessName, cchSize)) // _tcscpy_s(szProcessName, cchSize, TEXT("???")); //} //以上函数调用,可用下列函数来替换,因为下列函数可适用各种情况。 DWORD dwSize = (DWORD)cchSize; QueryFullProcessImageName(hProcess, 0, szProcessName, &dwSize); CloseHandle(hProcess); } ////////////////////////////////////////////////////////////////////////// DWORD WINAPI JobNotify(PVOID){ TCHAR sz[3000]; BOOL fDone = FALSE; ULONG_PTR CompKey; //指定触发这个事件的作业对象的句柄 LPOVERLAPPED po; //进程ID DWORD dwBytesXferred; //等待的事件ID while (!fDone){ GetQueuedCompletionStatus(g_hIOCP, &dwBytesXferred, &CompKey, &po, INFINITE); //应用程序关闭,退出线程 fDone = (CompKey == COMPKEY_TERMINATE); //lpClassName为 NULL则查找所有标题与第2个参数的匹配的窗口 HWND hwndLB = FindWindow(NULL, TEXT("Job Lab")); hwndLB = GetDlgItem(hwndLB, IDC_STATUS); if (CompKey == COMPKEY_JOBOBJECT) { _tcscpy_s(sz, _countof(sz), TEXT("--> Notification:")); PTSTR psz = sz + _tcslen(sz); switch (dwBytesXferred) { //作业对象中活动进程数达到上限 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("作业对象中活动进程数己达到上限!")); break; //作业对象中当前没有活动进程 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("作业对象中当前没有活动进程!")); break; //作业对象耗尽指定的时间片 case JOB_OBJECT_MSG_END_OF_JOB_TIME:{ StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("作业对象耗尽指定的时间片!")); } break; //作业对象耗尽指定的内存 case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:{ TCHAR szProcessName[MAX_PATH]; GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)导致作业对象消耗内存达到上限!"), szProcessName,po); } break; //新进程加入作业对象 case JOB_OBJECT_MSG_NEW_PROCESS:{ TCHAR szProcessName[MAX_PATH]; GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)加入作业对象!"), szProcessName, po); } break; //进程正常退出 case JOB_OBJECT_MSG_EXIT_PROCESS:{ TCHAR szProcessName[MAX_PATH]; DWORD dwExitCode = 0; //注意进程退出了,但其内核对象并没释放,还可以从内核对象中获取退出码 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PtrToUlong(po)); if (NULL != hProcess) { GetExitCodeProcess(hProcess, &dwExitCode); CloseHandle(hProcess); } GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)正常退出,退出码(%d)!"), szProcessName, po, dwExitCode); } break; //进程异常退出 case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:{ TCHAR szProcessName[MAX_PATH]; DWORD dwExitCode = 0; //注意进程退出了,但其内核对象并没释放,还可以从内核对象中获取退出码 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PtrToUlong(po)); if (NULL != hProcess) { GetExitCodeProcess(hProcess, &dwExitCode); CloseHandle(hProcess); } GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)异常退出,退出码(%d)!"), szProcessName, po, dwExitCode); } break; //进程耗尽时间片 case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:{ TCHAR szProcessName[MAX_PATH]; GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)耗尽时间片!"), szProcessName, po); } break; //进程消耗内存数量达到上限 case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:{ TCHAR szProcessName[MAX_PATH]; GetProcessName(PtrToUlong(po), szProcessName, MAX_PATH);//po存在进程ID StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("进程 %s(ID=%d)消耗内存达到上限!"), szProcessName, po); } break; default: StringCchPrintf(psz, _countof(sz) - _tcslen(sz), TEXT("未知通知:%d"), dwBytesXferred); break; } ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); CompKey = 1; //当作业通知到达时,强迫更新状态 } //更新作业状态 if (CompKey ==COMPKEY_STATUS) { static int s_nStatusCount = 0; StringCchPrintf(sz, _countof(sz), TEXT("-->状态更新(%u)"), s_nStatusCount++); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); //显示作业对象的基本统计信息 JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION jobai; g_job.QueryBasicAccountingInfo(&jobai); StringCchPrintf(sz, _countof(sz), TEXT("总时间:用户=%I64u,内核=%I64u ") TEXT("Period时间:用户=%I64u,内核=%I64u"), jobai.BasicInfo.TotalUserTime.QuadPart, jobai.BasicInfo.TotalKernelTime.QuadPart); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); StringCchPrintf(sz, _countof(sz), TEXT("页面错误=%u,总进程数=%u, ") TEXT("活动进程数=%u,己终止的进程数=%u"), jobai.BasicInfo.TotalPageFaultCount, jobai.BasicInfo.TotalProcesses, jobai.BasicInfo.ActiveProcesses, jobai.BasicInfo.TotalTerminatedProcesses); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); //显示I/O统计信息 StringCchPrintf(sz, _countof(sz), TEXT("读取=%I64u(I64u bytes), ") TEXT("写入=%I64u(I64u bytes),其它=%I64u(I64u bytes)"), jobai.IoInfo.ReadOperationCount,jobai.IoInfo.ReadTransferCount, jobai.IoInfo.WriteOperationCount,jobai.IoInfo.WriteTransferCount, jobai.IoInfo.OtherOperationCount,jobai.IoInfo.OtherTransferCount); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); //显示每个进程和作业的内存峰值 JOBOBJECT_EXTENDED_LIMIT_INFORMATION joeli; g_job.QueryExtendedLimitInfo(&joeli); StringCchPrintf(sz, _countof(sz), TEXT("己使用内存峰值:进程=%I64u,作业=%I64u"), (__int64)joeli.PeakProcessMemoryUsed, (__int64)joeli.PeakJobMemoryUsed); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); //显示进程ID集 const int iNum = 50; DWORD dwNumProcesses = iNum; DWORD dwProcessIDList[iNum]; g_job.QueryBasicProcessIdList(dwNumProcesses, dwProcessIDList, &dwNumProcesses); StringCchPrintf(sz, _countof(sz), TEXT("进程ID集:%s"), (dwNumProcesses == 0)?TEXT("(none)"):TEXT("")); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); TCHAR szProcessName[MAX_PATH]; for (DWORD x = 0; x < dwNumProcesses; x++) { GetProcessName(dwProcessIDList[x], szProcessName, _countof(szProcessName)); StringCchPrintf(sz, _countof(sz), TEXT(" %d - %s"), dwProcessIDList[x],szProcessName); ListBox_SetCurSel(hwndLB, ListBox_AddString(hwndLB, sz)); } } } return 0; } void Dlg_ApplayLimits(HWND hwnd){ const int nNanosecondsPerSecond = 1000000000;//1秒等于多少纳秒 const int nMillisecondsPerSecond = 1000; //1s=1000ms const int nNanosecondsPerMillisecond = //1ms 等于多少纳秒 nNanosecondsPerSecond / nMillisecondsPerSecond; __int64 q; SIZE_T s; DWORD d; BOOL f; //设置作业的基本\扩展限制 JOBOBJECT_EXTENDED_LIMIT_INFORMATION joeli = {}; joeli.BasicLimitInformation.LimitFlags = 0; //进程用户模式时间限制——f指出转换是否成功 q = GetDlgItemInt(hwnd, IDC_PERPROCESSUSERTIMELIMIT, &f, FALSE); if (f) { joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME; joeli.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = q*nNanosecondsPerMillisecond / 100; // ns/ms } //作业用户模式时间限制——f指出转换是否成功 q = GetDlgItemInt(hwnd, IDC_PERJOBUSERTIMELIMIT, &f, FALSE); if (f) { joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME; joeli.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = q*nNanosecondsPerMillisecond / 100; // ns/ms } //最小工作集 s = GetDlgItemInt(hwnd, IDC_MINWORKINGSETSIZE, &f, FALSE); if (f){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_WORKINGSET; joeli.BasicLimitInformation.MinimumWorkingSetSize = s * 1024 * 1024; //(MB) s = GetDlgItemInt(hwnd, IDC_MAXWORKINGSETSIZE, &f, FALSE); if (f){ joeli.BasicLimitInformation.MaximumWorkingSetSize = s * 1024 * 1024; //(MB) } else{ joeli.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_WORKINGSET; MessageBox(hwnd,TEXT("最小和最大工作集要同时设置。\n"),NULL,MB_OK|MB_ICONERROR); } } //最大同时活动进程数 d = GetDlgItemInt(hwnd, IDC_ACTIVEPROCESSLIMIT, &f, TRUE); if (f){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; joeli.BasicLimitInformation.ActiveProcessLimit = d; } //进程亲缘性掩码 s = GetDlgItemInt(hwnd, IDC_AFFINITYMASK, &f, FALSE); if (f){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY; joeli.BasicLimitInformation.Affinity = s; } //作业优先级 joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; switch (ComboBox_GetCurSel(GetDlgItem(hwnd,IDC_PRIORITYCLASS))) { case 0: joeli.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_PRIORITY_CLASS; break; case 1: joeli.BasicLimitInformation.PriorityClass = IDLE_PRIORITY_CLASS; break; case 2: joeli.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS; break; case 3: joeli.BasicLimitInformation.PriorityClass = NORMAL_PRIORITY_CLASS; break; case 4: joeli.BasicLimitInformation.PriorityClass = ABOVE_NORMAL_PRIORITY_CLASS; break; case 5: joeli.BasicLimitInformation.PriorityClass = HIGH_PRIORITY_CLASS; break; case 6: joeli.BasicLimitInformation.PriorityClass = REALTIME_PRIORITY_CLASS; break; } //SchedulingClass int nSchedulingClass = ComboBox_GetCurSel(GetDlgItem(hwnd, IDC_SCHEDULINGCLASS)); if (nSchedulingClass>0){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS; joeli.BasicLimitInformation.SchedulingClass = nSchedulingClass - 1; } //作业提交的最大物理页面 s = GetDlgItemInt(hwnd, IDC_MAXCOMMITPERJOB, &f, FALSE); if (f){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY; joeli.JobMemoryLimit= s * 1024*1024; } //进程的提交最大物理页面 s = GetDlgItemInt(hwnd, IDC_MAXCOMMITPERPROCESS, &f, FALSE); if (f){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; joeli.ProcessMemoryLimit= s * 1024 * 1024; } if (IsDlgButtonChecked(hwnd, IDC_CHILDPROCESSESCANBREAKAWAYFROMJOB)){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; } if (IsDlgButtonChecked(hwnd, IDC_CHILDPROCESSESDOBREAKAWAYFROMJOB)){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; } if (IsDlgButtonChecked(hwnd, IDC_TERMINATEPROCESSONEXCEPTIONS)){ joeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; } f = g_job.SetExtendedLimitInfo(&joeli, ((joeli.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME) !=0)?FALSE: IsDlgButtonChecked(hwnd,IDC_PRESERVEJOBTIMEWHENAPPLYINGLIMITS)); chASSERT(f);//调试版,!f弹对话框 //设置UI限制 DWORD jobuir = JOB_OBJECT_UILIMIT_NONE; //0; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTACCESSTOOUTSIDEUSEROBJECTS)) jobuir |= JOB_OBJECT_UILIMIT_HANDLES; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTREADINGCLIPBOARD)) jobuir |= JOB_OBJECT_UILIMIT_READCLIPBOARD; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTWRITINGCLIPBOARD)) jobuir |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTEXITWINDOW)) jobuir |= JOB_OBJECT_UILIMIT_EXITWINDOWS; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTCHANGINGSYSTEMPARAMETERS)) jobuir |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTDESKTOPS)) jobuir |= JOB_OBJECT_UILIMIT_DESKTOP; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTDISPLAYSETTINGS)) jobuir |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS; if (IsDlgButtonChecked(hwnd, IDC_RESTRICTGLOBALATOMS)) jobuir |= JOB_OBJECT_UILIMIT_GLOBALATOMS; chVERIFY(g_job.SetBasicUIRestrictions(jobuir)); } ////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_JOBLAB); //保存窗口句柄,以便在完成端口线程中可以访问到 g_hwnd = hwnd; HWND hwndPriortityClass = GetDlgItem(hwnd, IDC_PRIORITYCLASS); ComboBox_AddString(hwndPriortityClass, TEXT("No Limit")); ComboBox_AddString(hwndPriortityClass, TEXT("Idle")); ComboBox_AddString(hwndPriortityClass, TEXT("Below normal")); ComboBox_AddString(hwndPriortityClass, TEXT("Normal")); ComboBox_AddString(hwndPriortityClass, TEXT("Above normal")); ComboBox_AddString(hwndPriortityClass, TEXT("High")); ComboBox_AddString(hwndPriortityClass, TEXT("RealTime")); ComboBox_SetCurSel(hwndPriortityClass, 0); //默认选中“No Limit” HWND hwndSchedlingClass = GetDlgItem(hwnd, IDC_SCHEDULINGCLASS); ComboBox_AddString(hwndSchedlingClass, TEXT("No Limit")); for (int n = 0; n <= 9;n++) { TCHAR szSchedulingClass[2]; StringCchPrintf(szSchedulingClass, _countof(szSchedulingClass), TEXT("%u"),n); ComboBox_AddString(hwndSchedlingClass, szSchedulingClass); } ComboBox_SetCurSel(hwndSchedlingClass, 0); //默认选中“No Limit” SetTimer(hwnd, 1, 10000, NULL); //每10秒更新一次作业对象的统计信息 return TRUE; } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotify) { switch (id) { case IDCANCEL: //用户关闭应用程序并结束作业 KillTimer(hwnd, 1); g_job.Terminate(0); EndDialog(hwnd, id); break; case IDC_PERJOBUSERTIMELIMIT:{ //根据是否输入作业时间限制,来启用/禁用“调整限制时保留作业时间”复选框 BOOL f; GetDlgItemInt(hwnd, IDC_PERJOBUSERTIMELIMIT, &f, FALSE); EnableWindow(GetDlgItem(hwnd, IDC_PRESERVEJOBTIMEWHENAPPLYINGLIMITS), !f); } break; case IDC_APPLYLIMITS: Dlg_ApplayLimits(hwnd); PostQueuedCompletionStatus(g_hIOCP,0, COMPKEY_STATUS, NULL); break; case IDC_TERMINATE: g_job.Terminate(0); PostQueuedCompletionStatus(g_hIOCP, 0, COMPKEY_STATUS, NULL); break; case IDC_SPAWNCMDINJOB:{ //创建子进程 STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; TCHAR sz[] = TEXT("CMD"); CreateProcess(NULL, sz, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); g_job.AssignProcess(pi.hProcess); //这里本身会发出通知 ResumeThread(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); //PostQueuedCompletionStatus(g_hIOCP, 0, COMPKEY_STATUS, NULL); } break; case IDC_ASSIGNPROCESSTOJOB:{ //可以从任务管理器中查看某进程PID,并加入到该作业中来 DWORD dwProcessID = GetDlgItemInt(hwnd, IDC_PROCESSID, NULL, FALSE); //ROCESS_SET_QUOTA :使进程可以调用AssignProcessToJobObject加入作业和SetProcessWorkingSetSize设置内存限制 //PROCESS_TERMINATE:使进程可以调用TerminateProcess来结束程序 HANDLE hProcess = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, dwProcessID); if (hProcess != NULL){ chVERIFY(g_job.AssignProcess(hProcess)); CloseHandle(hProcess); } else MessageBox(hwnd, TEXT("该进程不能加入作业!"), TEXT("错误提示"), MB_OK | MB_ICONEXCLAMATION); PostQueuedCompletionStatus(g_hIOCP, 0, COMPKEY_STATUS, NULL); } break; } } ////////////////////////////////////////////////////////////////////////// void WINAPI Dlg_OnTimer(HWND hwnd, UINT id){ PostQueuedCompletionStatus(g_hIOCP, 0, COMPKEY_STATUS, NULL); } ////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_TIMER, Dlg_OnTimer); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return FALSE; } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR szCmdLine, int) { //创建完成端口来接收作业通知 //NumberOfConcurrentThreads表示访问该消息队列的线程数,为0时表示与处理器个数相等 g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); //创建1个线程来等待完成端口 g_hThreadIOCP = chBEGINTHREADEX(NULL, 0, JobNotify, NULL, 0, NULL); //创建一个作业对象 g_job.Create(NULL, TEXT("JobLab")); g_job.SetEndOfJobInfo(JOB_OBJECT_POST_AT_END_OF_JOB); g_job.AssociateCompletionPort(g_hIOCP, COMPKEY_JOBOBJECT); DialogBox(hInstExe, MAKEINTRESOURCE(IDD_JOBLAB), NULL, Dlg_Proc); //投递一个“退出”消息给完成端口,以便结束作业对象 PostQueuedCompletionStatus(g_hIOCP, 0, COMPKEY_TERMINATE, NULL); //等待完成端口结束各线程 WaitForSingleObject(g_hThreadIOCP, INFINITE); //清理 CloseHandle(g_hIOCP); CloseHandle(g_hThreadIOCP); return (0); }
//Job.h文件
/****************************************************************************** Module: Job.h Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre ******************************************************************************/ #pragma once ////////////////////////////////////////////////////////////////////////// #include <malloc.h> //for _alloca class CJob{ private: HANDLE m_hJob; public: CJob(HANDLE hJob = NULL); ~CJob(); operator HANDLE() const { return (m_hJob); } //重载()运算符 //用于打开或创建一个作业对象 BOOL Create(PSECURITY_ATTRIBUTES psa = NULL, PCTSTR pszName = NULL); BOOL Open(PCTSTR pszName, DWORD dwDesiredAccess, BOOL fInheritHandle = FALSE); //用来操作作业对象的函数 BOOL AssignProcess(HANDLE hProcess); BOOL Terminate(UINT uExitCode = 0); //用于设置或限制作业对象 BOOL SetExtendedLimitInfo(PJOBOBJECT_EXTENDED_LIMIT_INFORMATION pjoeli, BOOL fPreserveJobTime = FALSE); BOOL SetBasicUIRestrictions(DWORD fdwLimits); BOOL GrantUserHandleAccess(HANDLE hUserObj, BOOL fGrant = TRUE); //设置访问作业外用户对象能力 BOOL SetSecurityLimitInfo(PJOBOBJECT_SECURITY_LIMIT_INFORMATION pjosli); //查询作业限制信息 BOOL QueryExtendedLimitInfo(PJOBOBJECT_EXTENDED_LIMIT_INFORMATION pjoeli); BOOL QueryBasicUIRestrictions(PDWORD fdwRestrictions); BOOL QuerySecurityLimitInfo(PJOBOBJECT_SECURITY_LIMIT_INFORMATION pjosli); //查询作业状态信息 BOOL QueryBasicAccountingInfo(PJOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION pjobai); BOOL QueryBasicProcessIdList(DWORD dwMaxProcesses, PDWORD pdwProcessIdList, PDWORD pdwProcessesReturned = NULL); //设置或查询作业的通知事件 BOOL AssociateCompletionPort(HANDLE hIOCP, ULONG_PTR CompKey); BOOL QueryAssociatedCompletionPort(PJOBOBJECT_ASSOCIATE_COMPLETION_PORT pjoacp); BOOL SetEndOfJobInfo(DWORD fdwEndOfJobInfo = JOB_OBJECT_TERMINATE_AT_END_OF_JOB); BOOL QueryEndOfJobTimeInfo(PDWORD pfdwEndOfJobTimeInfo); }; ////////////////////////////////////////////////////////////////////////// inline CJob::CJob(HANDLE hJob){ m_hJob = hJob; } ////////////////////////////////////////////////////////////////////////// inline CJob::~CJob(){ if (NULL != m_hJob) CloseHandle(m_hJob); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::Create(PSECURITY_ATTRIBUTES psa, PCTSTR pszName){ m_hJob = CreateJobObject(psa, pszName); return (m_hJob != NULL); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::Open(PCTSTR pszName, DWORD dwDesiredAccess, BOOL fInheritHandle){ m_hJob = OpenJobObject(dwDesiredAccess, fInheritHandle, pszName); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::AssignProcess(HANDLE hProcess){ return (AssignProcessToJobObject(m_hJob, hProcess)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::Terminate(UINT uExitCode /* = 0 */){ return (TerminateJobObject(m_hJob, uExitCode)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::SetExtendedLimitInfo( PJOBOBJECT_EXTENDED_LIMIT_INFORMATION pjoeli, BOOL fPreserveJobTime /* = FALSE */){ if (fPreserveJobTime) pjoeli->BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME; //如果要保留作业时间信息,则JOB_OBJECT_LIMIT_JOB_TIME标志必须去掉 const DWORD fdwFlagTest = (JOB_OBJECT_LIMIT_JOB_TIME | JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME); if ((pjoeli->BasicLimitInformation.LimitFlags & fdwFlagTest) == fdwFlagTest) DebugBreak(); //这两个标志位是互斥的,但现在两者同时被设置了。 return (SetInformationJobObject(m_hJob, JobObjectExtendedLimitInformation, pjoeli,sizeof(*pjoeli))); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::SetBasicUIRestrictions(DWORD fdwLimits){ JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir = { fdwLimits }; return (SetInformationJobObject(m_hJob, JobObjectBasicUIRestrictions, &jobuir, sizeof(jobuir))); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::SetSecurityLimitInfo(PJOBOBJECT_SECURITY_LIMIT_INFORMATION pjosli){ return (SetInformationJobObject(m_hJob, JobObjectSecurityLimitInformation,pjosli, sizeof(*pjosli))); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::GrantUserHandleAccess(HANDLE hUserObj, BOOL fGrant /* = TRUE */){ return UserHandleGrantAccess(hUserObj, m_hJob, fGrant); //API } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryExtendedLimitInfo(PJOBOBJECT_EXTENDED_LIMIT_INFORMATION pjoeli){ return (QueryInformationJobObject(m_hJob,JobObjectExtendedLimitInformation, pjoeli,sizeof(*pjoeli),NULL)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryBasicUIRestrictions(PDWORD pfdwRestrictions){ JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir; BOOL fOk = QueryInformationJobObject(m_hJob, JobObjectBasicUIRestrictions, &jobuir, sizeof(jobuir), NULL); if (fOk) *pfdwRestrictions = jobuir.UIRestrictionsClass; return (fOk); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QuerySecurityLimitInfo(PJOBOBJECT_SECURITY_LIMIT_INFORMATION pjosli){ return (QueryInformationJobObject(m_hJob, JobObjectSecurityLimitInformation, pjosli, sizeof(*pjosli), NULL)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryBasicAccountingInfo(PJOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION pjobai){ return (QueryInformationJobObject(m_hJob, JobObjectBasicAndIoAccountingInformation, pjobai, sizeof(*pjobai),NULL)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryBasicProcessIdList(DWORD dwMaxProcesses, PDWORD pdwProcessIdList, PDWORD pdwProcessesReturned /* = NULL */){ //计算所需的空间大小 DWORD cb = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (sizeof(DWORD)*(dwMaxProcesses -1)); //从栈上分配内存(注意,不是堆,所有无需释放) PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpil = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)_alloca(cb); BOOL fOk = (pjobpil != NULL); if (fOk) { //告知函数,我们分配的最大空间大小 pjobpil->NumberOfProcessIdsInList = dwMaxProcesses; //请求返回当前进程集ID fOk = QueryInformationJobObject(m_hJob, JobObjectBasicProcessIdList,pjobpil,cb,NULL); if (fOk) { //得到信息,并返回给调用者 if (pdwProcessesReturned != NULL) *pdwProcessesReturned = pjobpil->NumberOfProcessIdsInList; CopyMemory(pdwProcessIdList, pjobpil->ProcessIdList, sizeof(DWORD)*pjobpil->NumberOfProcessIdsInList); } } return fOk; } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::AssociateCompletionPort(HANDLE hIOCP, ULONG_PTR CompKey){ JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp; joacp.CompletionPort = hIOCP; joacp.CompletionKey = (PVOID)CompKey; return (SetInformationJobObject(m_hJob, JobObjectAssociateCompletionPortInformation,&joacp,sizeof(joacp))); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryAssociatedCompletionPort(PJOBOBJECT_ASSOCIATE_COMPLETION_PORT pjoacp){ return (QueryInformationJobObject(m_hJob, JobObjectAssociateCompletionPortInformation,pjoacp,sizeof(*pjoacp),NULL)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::SetEndOfJobInfo(DWORD fdwEndOfJobInfo /* = JOB_OBJECT_TERMINATE_AT_END_OF_JOB */){ JOBOBJECT_END_OF_JOB_TIME_INFORMATION joeojti = { fdwEndOfJobInfo }; return (SetInformationJobObject(m_hJob, JobObjectEndOfJobTimeInformation, &joeojti, sizeof(joeojti))); } ////////////////////////////////////////////////////////////////////////// inline BOOL CJob::QueryEndOfJobTimeInfo(PDWORD pfdwEndOfJobTimeInfo){ JOBOBJECT_END_OF_JOB_TIME_INFORMATION joeojti; BOOL fOk = QueryInformationJobObject(m_hJob, JobObjectEndOfJobTimeInformation, &joeojti, sizeof(joeojti), NULL); if (fOk) *pfdwEndOfJobTimeInfo = joeojti.EndOfJobTimeAction; return (fOk); } ///////////////////////////// End of File //////////////////////////////////////////
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 05_JobLab.rc 使用 // #define IDI_JOBLAB 101 #define IDD_JOBLAB 102 #define IDC_CHILDPROCESSESCANBREAKAWAYFROMJOB 1001 #define IDC_CHILDPROCESSESDOBREAKAWAYFROMJOB 1002 #define IDC_TERMINATEPROCESSONEXCEPTIONS 1003 #define IDC_PRESERVEJOBTIMEWHENAPPLYINGLIMITS 1004 #define IDC_RESTRICTACCESSTOOUTSIDEUSEROBJECTS 1006 #define IDC_RESTRICTCHANGINGSYSTEMPARAMETERS 1007 #define IDC_RESTRICTREADINGCLIPBOARD 1008 #define IDC_RESTRICTDESKTOPS 1009 #define IDC_RESTRICTWRITINGCLIPBOARD 1010 #define IDC_RESTRICTDISPLAYSETTINGS 1011 #define IDC_RESTRICTEXITWINDOW 1012 #define IDC_RESTRICTGLOBALATOMS 1013 #define IDC_STATUS 1014 #define IDC_APPLYLIMITS 1015 #define IDC_PRIORITYCLASS 1016 #define IDC_TERMINATE 1017 #define IDC_SPAWNCMDINJOB 1018 #define IDC_ASSIGNPROCESSTOJOB 1019 #define IDC_SCHEDULINGCLASS 1020 #define IDC_PERPROCESSUSERTIMELIMIT 1021 #define IDC_PERJOBUSERTIMELIMIT 1022 #define IDC_MINWORKINGSETSIZE 1023 #define IDC_MAXWORKINGSETSIZE 1024 #define IDC_ACTIVEPROCESSLIMIT 1025 #define IDC_AFFINITYMASK 1026 #define IDC_MAXCOMMITPERJOB 1027 #define IDC_MAXCOMMITPERPROCESS 1028 #define IDC_PROCESSID 1029 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1023 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//5_JobLab.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_JOBLAB ICON "JobLab.ico" ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_JOBLAB DIALOGEX 0, 0, 305, 270 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU CAPTION "Job Lab" FONT 9, "MS Shell Dlg", 400, 0, 0x1 BEGIN GROUPBOX "基本和扩展限制",IDC_STATIC,16,7,274,120 CONTROL "进程用户时间上限(ms)",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,22,21,79,8 EDITTEXT IDC_PERPROCESSUSERTIMELIMIT,107,18,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "作业时间上限(ms)",IDC_STATIC,153,21,63,8 EDITTEXT IDC_PERJOBUSERTIMELIMIT,242,18,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "最小工作集(MB)",IDC_STATIC,22,36,56,8 EDITTEXT IDC_MINWORKINGSETSIZE,107,33,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "最大工作集(MB)",IDC_STATIC,153,36,56,8 EDITTEXT IDC_MAXWORKINGSETSIZE,242,34,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "同时活动进程数量",IDC_STATIC,22,51,65,8 EDITTEXT IDC_ACTIVEPROCESSLIMIT,107,48,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "进程亲缘性掩码(10进制)",IDC_STATIC,153,51,86,8 EDITTEXT IDC_AFFINITYMASK,242,50,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "优先级",IDC_STATIC,22,66,25,8 COMBOBOX IDC_PRIORITYCLASS,99,63,48,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "线程调度类型",IDC_STATIC,153,66,49,8 COMBOBOX IDC_SCHEDULINGCLASS,234,66,48,200,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "作业提交物理页面(MB)",IDC_STATIC,22,81,77,8 EDITTEXT IDC_MAXCOMMITPERJOB,107,78,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "进程提交物理页面(MB)",IDC_STATIC,153,81,81,8 EDITTEXT IDC_MAXCOMMITPERPROCESS,242,81,40,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER CONTROL "子进程可以作业中分离出来",IDC_CHILDPROCESSESCANBREAKAWAYFROMJOB, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,96,112,10 CONTROL "将子进程从作业中分离出来",IDC_CHILDPROCESSESDOBREAKAWAYFROMJOB, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,153,96,110,10 CONTROL "进程遇未知错误终止运行",IDC_TERMINATEPROCESSONEXCEPTIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,112,104,10 CONTROL "调整限制时保留作业时间",IDC_PRESERVEJOBTIMEWHENAPPLYINGLIMITS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,153,112,104,10 GROUPBOX "UI访问限制",IDC_STATIC,16,129,276,71 CONTROL "禁止访问外部用户对象",IDC_RESTRICTACCESSTOOUTSIDEUSEROBJECTS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,142,96,10 CONTROL "禁止改变系统参数",IDC_RESTRICTCHANGINGSYSTEMPARAMETERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,142,80,10 CONTROL "禁止读取剪贴板",IDC_RESTRICTREADINGCLIPBOARD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,156,72,10 CONTROL "禁止创建/切换桌面",IDC_RESTRICTDESKTOPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,156,83,10 CONTROL "禁止写入剪贴板",IDC_RESTRICTWRITINGCLIPBOARD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,170,72,10 CONTROL "禁止改变显示设置",IDC_RESTRICTDISPLAYSETTINGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,170,80,10 CONTROL "禁止调用ExitWindows函数",IDC_RESTRICTEXITWINDOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,184,105,10 CONTROL "禁用全局原语表",IDC_RESTRICTGLOBALATOMS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,184,72,10 LISTBOX IDC_STATUS,15,203,277,61,NOT LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "应用限制",IDC_APPLYLIMITS,217,136,66,14 PUSHBUTTON "终止所有进程",IDC_TERMINATE,217,152,66,14 PUSHBUTTON "创建CMD子进程",IDC_SPAWNCMDINJOB,217,168,66,14 PUSHBUTTON "PID加入作业",IDC_ASSIGNPROCESSTOJOB,231,184,51,14 EDITTEXT IDC_PROCESSID,205,185,25,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_JOBLAB, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 298 TOPMARGIN, 7 BOTTOMMARGIN, 267 END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED