
- •WINDOWS
- •Джеффри Рихтер
- •ЧАCTЬ I МАТЕРИАЛЫ ДЛЯ ОБЯЗАТЕЛЬНОГО ЧТЕНИЯ
- •ГЛАВА 1. Обработка ошибок
- •Вы тоже можете это сделать
- •Программа-пример ErrorShow
- •ГЛАВА 2 Unicode
- •Наборы символов
- •Одно- и двухбайтовые наборы символов
- •Unicode: набор широких символов
- •Почему Unicode?
- •Windows 2000 и Unicode
- •Windows 98 и Unicode
- •Windows CE и Unicode
- •В чью пользу счет?
- •Unicode и СОМ
- •Как писать программу с использованием Unicode
- •Unicode и библиотека С
- •Типы данных, определенные в Windows для Unicode
- •Unicode- и ANSI-функции в Windows
- •Строковые функции Windows
- •Ресурсы
- •Текстовые файлы
- •Перекодировка строк из Unicode в ANSI и обратно
- •ГЛАВА 3 Объекты ядра
- •Что такое объект ядра
- •Учет пользователей объектов ядра
- •Защита
- •Таблица описателей объектов ядра
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Совместное использование объектов ядра несколькими процессами
- •Наследование описателя объекта
- •Изменение флагов описателя
- •Именованные объекты
- •Пространства имен Terminal Server
- •Дублирование описателей объектов
- •ЧАСТЬ II НАЧИНАЕМ РАБОТАТЬ
- •ГЛАВА 4 Процессы
- •Ваше первое Windows-приложение
- •Описатель экземпляра процесса
- •Описатель предыдущего экземпляра процесса
- •Командная строка процесса
- •Переменные окружения
- •Привязка к процессорам
- •Режим обработки ошибок
- •Текущие диск и каталог для процесса
- •Текущие каталоги для процесса
- •Определение версии системы
- •Функция CreateProcess
- •Параметры pszApplicationName и pszCommandLine
- •Параметры psaProcess, psaThread и blnheritHandles
- •Параметр fdwCreate
- •Параметр pvEnvironment
- •Параметр pszCurDir
- •Параметр psiStartlnfo
- •Параметр ppiProclnfo
- •Завершение процесса
- •Возврат управления входной функцией первичного потока
- •Функция ExitProcess
- •Функция TerminateProcess
- •Когда все потоки процесса уходят
- •Что происходит при завершении процесса
- •Дочерние процессы
- •Запуск обособленных дочерних процессов
- •Перечисление процессов, выполняемых в системе
- •Программа-пример Processlnfo
- •ГЛАВА 5 Задания
- •Определение ограничений, налагаемых на процессы в задании
- •Включение процесса в задание
- •Завершение всех процессов в задании
- •Получение статистической информации о задании
- •Уведомления заданий
- •Программа-пример JobLab
- •ГЛАВА 6 Базовые сведения о потоках
- •В каких случаях потоки создаются
- •И в каких случаях потоки не создаются
- •Ваша первая функция потока
- •Функция CreateThread
- •Параметр psa
- •Параметр cbStack
- •Параметры pfnStartAddr и pvParam
- •Параметр fdwCreate
- •Параметр pdwThreadlD
- •Завершение потока
- •Возврат управления функцией потока
- •Функция ExitThread
- •Функция TerminateThread
- •Если завершается процесс
- •Что происходит при завершении потока
- •Кое-что о внутреннем устройстве потока
- •Некоторые соображения по библиотеке С/С++
- •Ой, вместо _beginthreadex я по ошибке вызвал CreateThread
- •Библиотечные функции, которые лучше не вызывать
- •Как узнать о себе
- •Преобразование псевдоописателя в настоящий описатель
- •ГЛАВА 7 Планирование потоков, приоритет и привязка к процессорам
- •Приостановка и возобновление потоков
- •Приостановка и возобновление процессов
- •Функция Sleep
- •Переключение потоков
- •Определение периодов выполнения потока
- •Структура CONTEXT
- •Приоритеты потоков
- •Абстрагирование приоритетов
- •Программирование приоритетов
- •Динамическое изменение уровня приоритета потока
- •Подстройка планировщика для активного процесса
- •Программа-пример Scheduling Lab
- •Привязка потоков к процессорам
- •ГЛАВА 8 Синхронизация потоков в пользовательском режиме
- •Кэш-линии
- •Более сложные методы синхронизации потоков
- •Худшее, что можно сделать
- •Критические секции
- •Критические секции: важное дополнение
- •Критические секции и спин-блокировка
- •Критические секции и обработка ошибок
- •Несколько полезных приемов
- •Не занимайте критические секции надолго
- •ГЛАВА 9 Синхронизация потоков с использованием объектов ядра
- •Wait-функции
- •Побочные эффекты успешного ожидания
- •События
- •Программа-пример Handshake
- •Ожидаемые таймеры
- •Ожидаемые таймеры и АРС-очередь
- •И еще кое-что о таймерах
- •Семафоры
- •Мьютексы
- •Отказ от объекта-мьютекса
- •Мьютексы и критические секции
- •Программа-пример Queue
- •Сводная таблица объектов, используемых для синхронизации потоков
- •Другие функции, применяемые в синхронизации потоков
- •Асинхронный ввод-вывод на устройствах
- •Функция WaitForlnputldle
- •Функция MsgWaitForMultipleObjects(Ex)
- •Функция WaitForDebugEvent
- •Функция SignalObjectAndWait
- •ГЛАВА 10 Полезные средства для синхронизации потоков
- •Реализация критической секции: объект-оптекс
- •Программа-пример Optex
- •Создание инверсных семафоров и типов данных, безопасных в многопоточной среде
- •Программа-пример lnterlockedType
- •Синхронизация в сценарии "один писатель/группа читателей"
- •Программа-пример SWMRG
- •Реализация функции WaitForMultipleExpressions
- •Программа-пример WaitForMultExp
- •ГЛАВА 11 Пулы потоков
- •Сценарий 1: асинхронный вызов функций
- •Сценарий 2: вызов функций через определенные интервалы времени
- •Программа-пример TimedMsgBox
- •Сценарий 3: вызов функций при освобождении отдельных объектов ядра
- •Сценарий 4; вызов функций по завершении запросов на асинхронный ввод-вывод
- •ГЛАВА 12 Волокна
- •Работа с волокнами
- •Программа-пример Counter
- •ЧАСТЬ III УПРАВЛЕНИЕ ПАМЯТЬЮ
- •Виртуальное адресное пространство процесса
- •Как адресное пространство разбивается на разделы
- •Увеличение раздела для кода и данных пользовательского режима до 3 Гб на процессорах x86 (только Windows 2000)
- •Закрытый раздел размером 64 Кб (только Windows 2000)
- •Раздел для общих MMF (только Windows 98)
- •Регионы в адресном пространстве
- •Передача региону физической памяти
- •Физическая память и страничный файл
- •Физическая память в страничном файле не хранится
- •Атрибуты защиты
- •Защита типа «копирование при записи»
- •Специальные флаги атрибутов защиты
- •Подводя итоги
- •Блоки внутри регионов
- •Особенности адресного пространства в Windows 98
- •Выравнивание данных
- •ГЛАВА 14 Исследование виртуальной памяти
- •Системная информация
- •Программа-пример Syslnfo
- •Статус виртуальной памяти
- •Программа-пример VMStat
- •Определение состояния адресного пространства
- •Функция VMQuery
- •Программа-пример VMMap
- •ГЛАВА 15 Использование виртуальной памяти в приложениях
- •Резервирование региона в адресном пространстве
- •Передача памяти зарезервированному региону
- •Резервирование региона с одновременной передачей физической памяти
- •В какой момент региону передают физическую память
- •Возврат физической памяти и освобождение региона
- •В какой момент физическую память возвращают системе
- •Программа-пример VMAIloc
- •Изменение атрибутов защиты
- •Сброс содержимого физической памяти
- •Программа-пример MemReset
- •Механизм Address Windowing Extensions (только Windows 2000)
- •Программа-пример AWE
- •ГЛАВА 16 Стек потока
- •Стек потока в Windows 98
- •Функция из библиотеки С/С++ для контроля стека
- •Программа-пример Summation
- •ГЛАВА 17 Проецируемые в память файлы
- •Проецирование в память EXE- и DLL-файлов
- •Статические данные не разделяются несколькими экземплярами EXE или DLL
- •Программа-пример Applnst
- •Файлы данных, проецируемые в память
- •Метод 1: один файл, один буфер
- •Метод 2: два файла, один буфер
- •Метод 3: один файл, два буфера
- •Метод 4: один файл и никаких буферов
- •Использование проецируемых в память файлов
- •Этап1: создание или открытие объекта ядра «файл»
- •Этап 2: создание объекта ядра «проекция файла»
- •Этап 3: проецирование файловых данных на адресное пространство процесса
- •Этап 4: отключение файла данных от адресного пространства процесса
- •Этапы 5 и 6: закрытие объектов «проекция файла» и «файл»
- •Программа-пример FileRev
- •Обработка больших файлов
- •Проецируемые файлы и когерентность
- •Базовый адрес файла, проецируемого в память
- •Особенности проецирования файлов на разных платформах
- •Совместный доступ процессов к данным через механизм проецирования
- •Файлы, проецируемые на физическую память из страничного файла
- •Программа-пример MMFShare
- •Частичная передача физической памяти проецируемым файлам
- •Программа-пример MMFSparse
- •ГЛАВА 18 Динамически распределяемая память
- •Стандартная куча процесса
- •Дополнительные кучи в процессе
- •Защита компонентов
- •Более эффективное управление памятью
- •Локальный доступ
- •Исключение издержек, связанных с синхронизацией потоков
- •Быстрое освобождение всей памяти в куче
- •Создание дополнительной кучи
- •Выделение блока памяти из кучи
- •Изменение размера блока
- •Определение размера блока
- •Освобождение блока
- •Уничтожение кучи
- •Использование куч в программах на С++
- •Другие функции управления кучами
- •ЧАСТЬ IV ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ
- •ГЛАВА 19 DLL: основы
- •DLL и адресное пространство процесса
- •Общая картина
- •Создание DLL-модуля
- •Что такое экспорт
- •Создание DLL для использования с другими средствами разработки (отличными от Visual C++)
- •Создание ЕХЕ-модуля
- •Что такое импорт
- •Выполнение ЕХЕ-модуля
- •ГЛАВА 20 DLL: более сложные методы программирования
- •Явная загрузка DLL и связывание идентификаторов
- •Явная загрузка DLL
- •Явная выгрузка DLL
- •Явное подключение экспортируемого идентификатора
- •Функция входа/выхода
- •Уведомление DLL_PROCESS_ATTACH
- •Уведомление DLL_PROCESS_DETACH
- •Уведомление DLL_THREAD_ATTACH
- •Уведомление DLL_THREAD_DETACH
- •Как система упорядочивает вызовы DIIMain
- •Функция DllMain и библиотека С/С++
- •Отложенная загрузка DLL
- •Программа-пример DelayLoadApp
- •Переадресация вызовов функций
- •Известные DLL
- •Перенаправление DLL
- •Модификация базовых адресов модулей
- •Связывание модулей
- •ГЛАВА 21 Локальная память потока
- •Динамическая локальная память потока
- •Использование динамической TLS
- •Статическая локальная память потока
- •Пример внедрения DLL
- •Внедрение DLL c использованием реестра
- •Внедрение DLL с помощью ловушек
- •Утилита для сохранения позиций элементов на рабочем столе
- •Внедрение DLL с помощью удаленных потоков
- •Программа-пример lnjLib
- •Библиотека lmgWalk.dll
- •Внедрение троянской DLL
- •Внедрение DLL как отладчика
- •Внедрение кода в среде Windows 98 через проецируемый в память файл
- •Внедрение кода через функцию CreateProcess
- •Перехват API-вызовов: пример
- •Перехват API-вызовов подменой кода
- •Перехват API-вызовов с использованием раздела импорта
- •Программа-пример LastMsgBoxlnfo
- •ЧАСТЬ V СТРУКТУРНАЯ ОБРАБОТКА ИСКЛЮЧЕНИЙ
- •ГЛАВА 23 Обработчики завершения
- •Примеры использования обработчиков завершения
- •Funcenstein1
- •Funcenstein2
- •Funcenstein3
- •Funcfurter1
- •Проверьте себя: FuncaDoodleDoo
- •Funcenstein4
- •Funcarama1
- •Funcarama2
- •Funcarama3
- •Funcarama4: последний рубеж
- •И еще о блоке finally
- •Funcfurter2
- •Программа-пример SEHTerm
- •ГЛАВА 24 Фильтры и обработчики исключений
- •Примеры использования фильтров и обработчиков исключений
- •Funcmeister1
- •Funcmeister2
- •EXCEPTION_EXECUTE_HANDLER
- •Некоторые полезные примеры
- •Глобальная раскрутка
- •Остановка глобальной раскрутки
- •EXCEPTION_CONTINUE_EXECUTION
- •Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION
- •EXCEPTION_CONTINUE_SEARCH
- •Функция GetExceptionCode
- •Функция GetExceptionlnformation
- •Программные исключения
- •ГЛАВА 25 Необработанные исключения и исключения С++
- •Отладка по запросу
- •Отключение вывода сообщений об исключении
- •Принудительное завершение процесса
- •Создание оболочки вокруг функции потока
- •Создание оболочки вокруг всех функций потоков
- •Автоматический вызов отладчика
- •Явный вызов функции UnhandledExceptionFilter
- •Функция UnhandledExceptionFilter изнутри
- •Исключения и отладчик
- •Программа-пример Spreadsheet
- •Исключения С++ и структурные исключения
- •Перехват структурных исключений в С++
- •ЧАСТЬ VI ОПЕРАЦИИ С ОКНАМИ
- •ГЛАВА 26 Оконные сообщения
- •Очередь сообщений потока
- •Посылка асинхронных сообщений в очередь потока
- •Посылка синхронных сообщений окну
- •Пробуждение потока
- •Флаги состояния очереди
- •Алгоритм выборки сообщений из очереди потока
- •Пробуждение потока с использованием объектов ядра или флагов состояния очереди
- •Передача данных через сообщения
- •Программа-пример CopyData
- •ГЛАВА 27 Модель аппаратного ввода и локальное состояние ввода
- •Поток необработанного ввода
- •Локальное состояние ввода
- •Ввод с клавиатуры и фокус
- •Управление курсором мыши
- •Подключение к очередям виртуального ввода и переменным локального состояния ввода
- •Программа-пример LISLab
- •Программа-пример LISWatch

typedef struct _JOBOBJECT_SECURITY_LIMIT_INFORMATION
{
DWORD SecurityLimitFlags; HANDLE JobToken;
PTOKEN GROUPS SidsToDisable; PTOKEN_PRIVILEGES PrivilegesToDelete; PTOKEN_GROUPS RestrictedSids;
} JOBOBJECT_SECURITY LIMIT_INFORMATION,
*PJOBOBJECT_SECURITY_LIMIT_INFORMATION;
Ее элементы описаны в следующей таблице
Элемент |
Описание |
|
|
SecurityLimitFlags |
Набор флагов, которые закрывают доступ администратору, запре щают маркер |
|
неограниченного доступа, принудительно назначают заданный маркер доступа, |
|
блокируют доступ по каким-либо иден тификаторам защиты (security ID, SID) |
|
и отменяют указанные при вилегии |
JobToken |
Маркер доступа, связываемый со всеми процессами в задании |
|
|
SidsToDisable |
Указывает, по каким SID не разрешается доступ |
|
|
PrivilegesToDelete |
Определяет привилегии, которые снимаются с маркера доступа |
|
|
RestrictedSids |
Задает набор SID, по которым запрещается доступ к любому защи щенному |
|
объекту (deny-only SIDs); этот набор добавляется к марке ру доступа |
|
|
Естественно, если Вы налагаете ограничения, то потом Вам, наверное, понадобится информация о них. Для этого вызовите:
BOOL QueryInformationJobObject( HANDLE hJob, JOBOBJECTINFOCLASS JobObjectInformationClass. PVOID pvJobObjectInformation, DWORD cbJobObjectInformationLength, PDWORD pdwReturnLength);
В эту функцию, как и в SetInformationJobObject, передается описатель задания, пе ременная перечислимого типа JOBOJECTINFOCLASS. Она сообщает информацию об ограничениях, адрес и размер структуры данных, инициализируемой функцией. Пос ледний параметр, pdwReturnLength, заполняется самой функцией и указывает, сколь ко байтов помещено в буфер Если эти сведения Вас не интересуют (что обычно и бывает), передавайте в этом параметре NULL.
NOTE:
Процесс может получить информацию о своем задании, передав при вызове QuerylnformationJobObject вместо описателя задания значение NULL, Это позво лит ему выяснить установленные для него ограничения Однако аналогичный вызов SetInformationJobOtject даст ошибку, так как процесс не имеет права са мостоятельно изменять заданные для него ограничения
Включение процесса в задание
О'кэй, с ограничениями па этом закончим Вернемся к StartRestrictedProcess. Устано вив ограничения для задания, я вызываю CreateProcess и создаю процесс, который помещаю в это задание. Я использую здесь флаг CREATE_SUSPENDED, и он приводит к тому, что процесс порождается, но код пока не выполняет. Поскольку StartRestricted Process вызывается из процесса, внешнего по отношению к заданию, его дочерний
процесс тоже не входит в это задание. Если бы я разрешил дочернему процессу не медленно начать выполнение кода, он проигнорировал бы мою песочницу со всеми ее

ограничениями. Поэгому сразу после создания дочернего процесса и перед нача лом его работы я должен явно включить этот процесс в только что сформированное задание, вызвав:
BOOL AssignProcessToJobObject( HANDLE hJob, HANDLE hProcess);
Эта функция заставляет систему рассматривать процесс, идентифицируемый па раметром hProcess, как часть существующего задания, на которое указывает hJob. Об ратите внимание, что AssignProcessToJobObject позволяет включить в задание только тот процесс, который еще не относится ни к одному заданию. Как только процесс стал частью какого-нибудь задания, его нельзя переместить в другое задание или отпус тить на волю. Кроме того, когда процесс, включенный в задание, порождает новый процесс, последний автоматически помещается в то же задание. Однако этот поря док можно изменить.
Включая в LimitFlags структуры JOBOBJECT_BASIC_LIMIT_INFORMATION
флаг JOB_OBJECT_BREAKAWAY_OK, Вы сообщаете системе, что новый процесс мо жет выполняться вне задания. Потом Вы должны вызвать CreateProcess с новым флагом CREATE_BREAKAWAY_FROM_TOB. (Если Вы сделаете это без флага
JOB_OBJECT_BREAKAWAY_OK в LimitFlags, функция CreateProcess завершится с ошибкой.) Такой механизм пригодится на случай, если новый процесс тоже управляет заданиями.
Включая в LimitFlags структуры JOBOBJECT_BASIC_LIMIT_INFORMATION флаг JOB_OBJECT_SILENT_BREAKAWAY_OK, Вы тоже сообщаете системе, что новый процесс не является частью задания, Но указывать в CreateProcess какиелибо флаги на этот раз не потребуется. Данный механизм полезен для процессов, которым ничего не известно об объектах-заданиях.
Что касается StartRestrictedProcess, то после вызова AssignProcessToJobObject новый процесс становится частью задания. Далее я вызываю ResumeThread, чтобы поток нового процесса начал выполняться в рамках ограничений, установлепных для зада ния. В этот момент я также закрываю описатель потока, поскольку он мне больше не нужен.
Завершение всех процессов в задании
Уверен, именно это Вы и будете делать чаще всего. В начале главы я упомянул о том, как непросто остановить сборку в Developer Studio, потому что для этого ему должны быть известны все процессы, которые успел создать его самый первый процесс. (Это очень каверзная задача. Как Developer Studio справляется с ней, я объяснял в своей колонке «Вопросы и ответы по Win32» в июньском выпуске Microsoft Systems Journal за 1998 год.) Подозреваю, что следующие версии Developer Studio будут использовать механизм заданий, и решать задачу, о которой мы с Вами говорили, станет гораздо легче.
Чтобы уничтожить все процессы в задании, Вы просто вызываете
BOOL TerminateJobOb]ect( HANDLE hJob, UINT uExitCode);
Вызов этой функции похож на вызов TerminateProcessw для каждого процесса в за дании и присвоение всем кодам завершения одного значения — uExitCode.
Получение статистической информации о задании

Мы уже обсудили, как с помощью QueryInformationJobObject получить информацию о текущих ограничениях, установленных для задания. Этой функцией можно пользо ваться и для получения статистической информации. Например, чтобы выяснить ба зовые учетные сведения, вызовите ее, передав JobObjeсtBasicAccountingInformation во втором параметре и адрес структуры JOBOBJECT_BASIC_ACCOUNTING_INFORMATION:
typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
{
LARGE_INTEGER TotalUserTime; LARGE_INTEGER TotalKernelTime; LARGE_INTEGER ThisPeriodTotalUserTime; LARGE_INTEGER ThisPeriodTotalKernelTime; DWORD TotalPageFaultCount;
DWORD TotalProcesses;
DWORD ActiveProcesses;
DWORD TotalTerminatedProcesses;
} JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, *PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION;
Элементы этой структуры кратко описаны в таблице 5-3
Элемент |
Описание |
|
|
TotalUserTtme |
Процессорное время, израсходованное процессами задания в |
|
пользовательском режиме |
|
|
TotalKernelTime |
Процессорное время, израсходованное процессами задания в режиме ядра |
|
|
ThisPeriodTotalUserTime |
То же, что TotalUserTime, но обнуляется, когда базовые oгpa ничения |
|
изменяются вызовом SetIniformationJobObject, а флаг JOB |
|
OBJECT_LIMIT_PRESERVE_JOB_TIME не используется |
|
|
ThisPeriodTotalKernelTime |
То же, что ThisPeriodTotalUserTime, но относится к процессор ному |
|
времени, израсходованному в режиме ядра |
|
|
TotalPageFaultCount |
Общее количество ошибок страниц, вызванных процессами задания |
|
|
TotalProcesses |
Общее число процессов, когда-либо выполнявшихся в зтом задании |
|
|
ActiveProcesses |
Текущее количество процессов в задании |
|
|
TotalTermtnatedProcesses |
Количество процессов, завершенных из-за превышения ими отведенного |
|
лимита процессорного времени |
|
|
Таблица 5-3. Элементы структуры JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
Вы можете извлечь те же сведения вместе с учетной информацией по вводу-выво ду,
передав JobObjectBasicAndIoAccountingInformation во втором параметре и адрес структуры
JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION:
typedef struct JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION
{
JOBOBJECT_BASIC_ACCOUNTING_TNFORMATION Basiclnto; IO_COUNTERS IoInfo;
} JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION;
Как видите, она просто возвращает JOBOBJECT_BASIC_ACCOUNTlNG_INFORMA
TION и IO_COUNTERS. Последняя структура показана на следующей странице
typedef struct _IO_COUNTERS
{
ULONGlONG ReadOperationCount;
ULONGLONG WriteOperationCount;
ULONGLONG OtherOperationCount;
ULONGLONG ReadTransferCount;
ULONGLONG WriteTransferCount;
ULONGLONG OtheiTransferCount;
} IO_COUNTERS;
Она сообщает о числе операций чтения, записи и перемещения (а также о коли честве байтов, переданных при выполнении этих операций) Данные относятся ко всем процессам в задании Кстати, новая функция GetProcessIoCounters позволяет по лучить ту же информацию о процессах, не входящих ни в какие задания
BOOL GetProcessIoCounters( HANDLE hProcess, PIO_GOUNTERS pToCounters);
QueryInformationJobObject такжe возвращает набор идентификаторов текущих про цессов в задании Но перед этим Вы должны прикинуть, сколько их там может быть, и выделить соответствующий блок памяти, где поместятся массив идентификаторов и структура
JOBOBJECT_BASIC_PROCESS_ID_LIST
typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST
{
DWORD NumberOfAssigncdProcessps;
DWORD NurrberOfProcessIdsInList; DWORD ProcessIdList[1];
} JOBOBJECT_BASIC_PROCESS_ID_LIST,
*PJOBOBJECT_BASIC_PROCESS_ID_LIST ;
В итоге, чтобы получить набор идентификаторов текущих процессов в задании, нужно написать примерно такой код
void EnumProcessIdsInJob(HANDLE hjob)
{
//я исхожу из того, что количество процессов
//в этом задании никогда не превысит 10
#define MAX_PROCESS_TDS 10
//определяем размер блока памяти (в байтах)
//для хранения идентификаторов и структуры
DWORD cb = sizeof(JOBOBJECT_BASlC_PROCESS_ID LIST) + (MAX_PROCESS_IDS - 1) * sizeof(DWORD);
// выделяем этот блок памяти
PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpil = _alloca(cb);
//сообщаем функции, на какое максимальное число процессов
//рассчитана выделенная нами память pjobpil- >NumberOfAssignedProcesseb = MAX_PROCESS_IDS;
//запрашиваем текущий список идентификаторов процессов
QuerylnformationJobObject(hjob, JobObjectBasicProcessIdList pjobpil, cb &cb);
//перечисляем идентификаторы процессов
for (int x =- 0; x < pjobpil->NumberOfProcessIdsInList; x++)
{

// используем pjobpil->ProcessIdList[x]
}
//так как для выделения памяти мы вызывали _alloca,
//освобождать память нам не потребуется
}
Вот и все, что Вам удастся получить через эти функции, хотя на самом деле опе рационная система знает о заданиях гораздо больше. Эту информацию, которая хра нится в специальных счетчиках, можно извлечь с помощью функций из библиотеки Performance Data Helper (PDH dIl) или через модуль Performance Monitor, подключае мый к Microsoft Management Console (MMC) Рис 5-3 иллюстрирует некоторые из доступных в системе счетчиков заданий (job object counters), а рис. 5-4 — счетчики, относящиеся к отдельным параметрам заданий (job object details counters) Заметьте, что в чадании Jeff содержится четыре процесса calc, cmd, notepad и wordpad.
Рис. 5-3. MMC Performance Monitor счетчики задания