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


    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] = '';
                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
    "),
                         GetLastError());
                continue;
            }
    
            switch (dwReasonID)
            {
                //作业对象中活动进程数达到上限
            case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
                _tprintf(_T("作业对象中活动进程数己达到上限!
    "));
                break;
                
                //作业对象中当前没有活动进程
            case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
                _tprintf(_T("作业对象中当前没有活动进程!
    "));
                break;
    
                //作业对象耗尽指定的时间片
            case JOB_OBJECT_MSG_END_OF_JOB_TIME:
                _tprintf(_T("作业对象耗尽指定的时间片!
    "));
                break;
    
                //作业对象耗尽指定的内存
            case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:
                dwProcessID = (DWORD)lpOverlapped;
                _tprintf(_T("进程[ID:%u]导致作业对象消耗内存达到上限!
    "),dwProcessID);
                break;
    
                //新进程加入作业对象
            case JOB_OBJECT_MSG_NEW_PROCESS:
                dwProcessID = (DWORD)lpOverlapped;
                _tprintf(_T("进程[ID:%u]加入作业对象[h:0x%08X]!
    "), 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!
    "), 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!
    "), dwProcessID, dwExitCode);
                }
                break;
    
                //进程耗尽时间片
            case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
                dwProcessID = (DWORD)lpOverlapped;
                _tprintf(_T("进程[ID:%u]耗尽时间片!
    "), dwProcessID);
                break;
    
                //进程消耗内存数量达到上限
            case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:
                dwProcessID = (DWORD)lpOverlapped;
                _tprintf(_T("进程[ID:%u]消耗内存达到上限!
    "), dwProcessID);
                break;
    
            default:
                bLoop = FALSE;
                break;
    
            }
        }
        _tprintf(_T("ICOP线程(ID:0x%x退出)
    "), GetCurrentThreadId());
        return 0;
    }

     【JobLab程序】演示作业限制设置

    /******************************************************************************
    Module:  JobLab.cpp
    Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
    ******************************************************************************/
    
    #include "..\..\CommonFilesCmnHdr.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可能会失败。
        //    
        //    //调用失败,可能是文件名复杂的路径,诸如:
        //  //DeviceHarddiskVolume1WindowsSystem32
    otepad.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("最小和最大工作集要同时设置。
    "),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"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    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

     

     

  • 相关阅读:
    prometheus基础概念
    Prometheus告警处理
    什么是prometheus?
    Prometheus的PromQL
    Prometheus的Exporter详解
    leetcode unique path I&&II
    leetcode Palindrome Partitioning
    leetcode 最大子矩阵(5星推荐)
    leetcode Sum Root to Leaf Numbers 二叉树所有叶节点的路径和
    leetcode Spiral Matrix I
  • 原文地址:https://www.cnblogs.com/5iedu/p/4690974.html
Copyright © 2020-2023  润新知