您的位置:首页 > 其它

第5章 作业和进程池(2)

2015-07-30 23:30 330 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: