Параметр ppiProclnfo
Параметр ppiProcInfo указывает на структуру PROCESS_INFORMATION, которую Вы должны предварительно создать; ее элементы инициализируются самой функцией CreateProcess. Структура представляет собой следующее:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_TNFORMATION;
Как я уже говорил, создание нового процесса влечет за собой создание объектов ядра "процесс" и "поток". В момент создания система присваивает счетчику каждого объекта начальное значение — единицу. Далее функция CreateProcess (перед самым возвратом управлении) открывает объекты "процесс" и "поток" и заносит их описатели, специфичные для данного процесса, в элементы hProcess и hTbread структуры PROCESS_INFORMATION. Когда CreateProcess открывает эти объекты, счетчики каждого из них увеличиваются до 2.
Это означает, что, перед тем как система сможет высвободить из памяти объект "процесс", процесс должен быть завершен (счетчик уменьшен до 1), а родительский процесс обязан вызвать функцию CloseHandle (и тем самым обнулить счстчик). То же самое относится и к объекту "поток" поток должен быть завершен, а родительский процесс должен закрыть описатель объекта "поток". Подробнее об освобождении объектов "поток" см. раздел "Дочерние процессы" в этой главе.
NOTE
Не забывайте закрывать описатели дочернего процесса и его первичного по тока, иначе, пока Вы не закроете свое приложение, будет происходить утечка ресурсов. Конечно, система высвободит все эти ресурсы после завершения Вашего процесса, но хорошо написанная программа должна сама закрывать описатели дочернего процесса и его первичного потока, как только необходимость в них отпадает. Пропуск этой операции — одна из самых частых ошибок.
Почему-то многие разработчики считают, будто закрытие описателя процесса или потока заставляет систему уничтожить этот процесс или поток. Это абсолютно неправильно.
Закрывая описатель, Вы просто сообщаете системе, что статистические данные для этого процесса или потока Вас больше не интересуют, но процесс или поток продолжает исполняться системой до тех пор, пока он сам не завершит себя.
Созданному объекту ядра "процесс" присваивается уникальный идентификатор; ни у каких других объектов этого типа в системе не может быть одинаковых идентификаторов. Это же касается и объектов ядра "поток". Причем идентификаторы процесса и потока тоже разные, и их значения никогда не бывают нулевыми. Завершая свою работу, CreateProcess заносит значения идентификаторов в элементы divProcessId и dwThreadld структуры PROCESS_INFORMATION. Эти идентификаторы просто облегчают определение процессов и потоков в системе; их используют, как правило, лишь специализированные утилиты вроде Task Manager.
Подчеркну ещс один чрезвычайно важный момент система способна повторно использовать идентификаторы процессов и потоков. Например, при создании процесса система формирует объект "процесс", присваивая ему идентификатор со значением, допустим, 122. Создавая новый объект "процесс", система уже не присвоит ему данный идентификатор. Но после выгрузки из памяти первого объекта следующему создаваемому объекту "процесс" может быть присвоен тот же идентификатор — 122.
Эту особенность нужно учитывать при написании кода, избегая ссылок на неверный объект "процесс" (или "поток"). Действительно, затребовать и сохранить идентификатор процесса несложно, но задумайтесь, что получится, если в следующий момент этот процесс будет завершен, а новый получит тот же идентификатор: сохраненный ранее идентификатор уже связан совсем с другим процессом.
Иногда программе приходится определять свой родительский процесс. Однако родственные связи между процессами существуют лишь на стадии создания дочернего процесса. Непосредственно перед началом исполнения кода в дочернем процессе Windows перестает учитывать его родственные связи.
В предыдущих версиях Windows не было функций, которые позволяли бы программе обращаться с запросом к ее родительскому процессу. Но ToolHelp-функции, появившиеся в современных версиях Windows, сделали это возможным. С этой целью Вы должны использовать структуру PROCESSENTRY32: ее элемент th32ParentProcessID возвращает идентификатор "родителя" данного процесса. Тем не менее, если Вашей программе нужно взаимодействовать с родительским процессом, от идентификаторов лучше отказаться. Почему — я уже говорил. Для определения родительского процесса существуют более надежные механизмы: объекты ядра, описатели окон и т.д.
Единственный способ добиться того, чтобы идентификатор процесса или потока не использовался повторно, — не допускать разрушения объекта ядра "процесс" или "поток". Если Вы только что создали новый процесс или поток, то можете просто не закрывать описатели на зти объекты — вот и все. А по окончании операций с идентификатором, вызовите функцию CloseHandle и освободите соответствующие объекты ядра. Однако для дочернего процесса этот способ не годится, если только он не унаследовал описатели объектов ядра от родительского процесса.