- •1. Понятие процесса.
- •2.Типы приложений под Windows
- •3.Описатель экземпляра процесса
- •4.Описатель предыдущего экземпляра процесса
- •5.Командная строка процесса
- •6.Переменные окружения
- •7.Текущие диск и каталог для процесса
- •8.Текущие каталоги для процесса
- •9.Создание процесса - функция CreateProcess
- •9.1. Параметры pszApplicationName и pszCommandLine
- •9.2. Параметры psaProcess, psaThread и blnheritHandles
- •9.3. Параметр fdwCreate
- •9.4. Параметр pvEnvironment
- •9.5.Параметр pszCurDir
- •9.6. Параметр psiStartlnfo
- •9.7. Параметр ppiProclnfo
- •10. Завершение процесса
- •10.1. Возврат управления входной функцией первичного потока
- •10.2. Функция ExitProcess
- •10.3. Функция TerminateProcess
- •10.4.Завершение всех процессов потока
- •11. Порядок завершения процесса
- •14. Дочерние процессы
- •15. Запуск обособленных дочерних процессов
- •16. Порядок выполнения работы
- •17. Контрольные вопросы
9.1. Параметры pszApplicationName и pszCommandLine
Эти параметры определяют имя исполняемого файла, которым будет пользоваться новый процесс, и командную строку, передаваемую этому процессу. Начнем cpszComandLine.
Замечание: Следует отметить, что тип параметра pszCommandLine: PTSTR. Он означает, что CreateProcess ожидает передачи адреса строки, которая не является константой. Дело в том, что CreateProcess в процессе своего выполнения модифицирует переданную командную строку, но перед возвратом управления восстанавливает ее.
Например, следующий код приведет к такой ошибке, потому что Visual С++ 6.0 поместит строку «NOTEPAD» в память только для чтения:
STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
Когда CreateProcess попытается модифицировать строку, произойдет ошибка доступа. (В прежних версиях Visual C++ эта строка была бы размещена в памяти для чтения и записи, и вызовы CreateProcess не приводили к ошибкам доступа.)
Лучший способ решения этой проблемы — перед вызовом CreateProcess копировать константную строку во временный буфер:
STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; TCHAR szCommandLine[] = TEXT("NOTEPAD"); CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULI, NULL, &si, &pi);
Кстати, при вызове ANSI-версии CreateProcess в Windows 2000 таких проблем нет, поскольку в этой версии функции командная строка копируется во временный буфер.
Параметр pszCommandLine позволяет указать полную командную строку, используемую функцией CreateProcess при создании нового процесса, только если параметр pszApplicationName равен NULL (что и бывает в 99% случаев). Вместо NULL можно передать адрес строки с именем исполняемого файла, который надо запустить. Однако тогда придется указать не только его имя, но и расширение, поскольку в этом случае имя не дополняется расширением EXE автоматически. CreateProcess предполагает, что файл находится в текущем каталоге (если полный путь не задан). Если в текущем каталоге файла нет, функция не станет искать его в других каталогах, и на этом все закончится.
Но даже при указанном в pszApplicationName имени файла CreateProcess все равно передает новому процессу содержимое параметра pszCommandLine как командную строку. Допустим, вызвали CreateProcess так:
// размещаем строку пути в области памяти для чтения и записи TCHAR szPath[] = TEXT("WORDPAD README.TXT");
// порождаем новый процесс CreateProcess(TEXT("C:\\WINNT\\SYSTEM32\\NOTEPAD EXE"), szPath,... );
Система запускает Notepad, а в его командной строке мы видим "WORDPAD README.TXT". Но так уж она работает, эта функция CreateProcess. Упомянутая возможность, которую обеспечивает параметр pszApplicationName, на самом деле введена в CreateProcess для поддержки подсистемы POSIX в Windows 2000.
9.2. Параметры psaProcess, psaThread и blnheritHandles
Чтобы создать новый процесс, система должна сначала создать объекты ядра «процесс» и «поток» (для первичного потока процесса). Поскольку это объекты ядра, родительский процесс получает возможность связать с ними атрибуты защиты. Параметры psaProcess и psaThread позволяют определить нужные атрибуты защиты для объектов «процесс» и «поток» соответственно. В эти параметры можно занести NULL, и система закрепит за данными объектами дескрипторы защиты по умолчанию. В качестве альтернативы можно объявить и инициализировать две структуры SECURITY_ATTRIBUTES; тем самым создаются и присваиваются свои атрибуты защиты объектам «процесс» и «поток».
Структуры SECURITY_ATTRIBUTES для параметров psaProcess psaThread используются и для того, чтобы какой-либо из этих двух объектов получил статус наследуемого любым дочерним процессом.
Короткая программа см.ниже демонстрирует, как наследуются описатели объектов ядра. Будем считать, что процесс А порождает процесс В и заносит в параметр psaProcess адрес структуры SECURITY_ATTRIBUTES, в которой элемент blnheritHandle установлен как TRUE. Одновременно параметр psaThread указывает на другую структуру SECURITY_ATTRIBUTES, в которой значение элемента bInheritHandle — FALSE.
Inherit.c
/************************************************************
Module name: Inherit.c
Notices: Copyright (c) 2000 Jeffrey Richter
************************************************************/
#include <Windows.h>
int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE,
PSTR pszCmdLine, int nCmdShow) {
// Подготовка STARTUPINFO стркутуры для порождения процессов.
STARTUPINFO si = { sizeof(si) };
SECURITY_ATTRIBUTES saProcess, saThread;
PROCESS_INFORMATION piProcessB, piProcessC;
TCHAR szPath[MAX_PATH];
// Подготовка к порождению процесса B из процесса A.
// Дескриптор идентифицирует новый процесс
// объект должен быть наследуемым.
saProcess.nLength = sizeof(saProcess);
saProcess.lpSecurityDescriptor = NULL;
saProcess.bInheritHandle = TRUE;
// Дескриптор идентифицирует новый поток
// объект должен быть не наследуемым.
saThread.nLength = sizeof(saThread);
saThread.lpSecurityDescriptor = NULL;
saThread.bInheritHandle = FALSE;
// Порождение процесса B.
lstrcpy(szPath, TEXT("ProcessB"));
CreateProcess(NULL, szPath, &saProcess, &saThread,
FALSE, 0, NULL, NULL, &si, &piProcessB);
// Стркутура piProcessB содержит два дескриптора
// относящихся к процессу A:
// hProcess, который идентифицирует наследуемый объект процесса B
// и hThread, который идентифицирует объект ядра – главный поток
// процесса B и не является наследуемым.
// Подготовка к порождению процесса C из процесса A.
// Так NULL передается для параметров psaProcess и psaThread
// , дескрипторы к объектам процесс и
// главный поток процесса С – по умолчанию ненаследуемые
// Если процесс A породит другой новый процесс, этот новый процесс
// не наследует дескрипторы к объектам процесса и потка процесса С.
// Так TRUE передается в качестве параметра bInheritHandles,
// Процесс C будет наследовать десриптор идентифицирующий процесс
// B но не будет наследовать дескриптор его главного потока.
lstrcpy(szPath, TEXT("ProcessC"));
CreateProcess(NULL, szPath, NULL, NULL,
TRUE, 0, NULL, NULL, &si, &piProcessC);
return(0);
}
Создавая процесс В, система формирует объекты ядра «процесс» и «поток», а за тем — в структуре, на которую указывает параметр ppiProcInfo, — возвращает их описатели процессу А, и с этого момента тот может манипулировать только что созданными объектами «процесс» и «поток».
Теперь предположим, что процесс А собирается вторично вызвать функцию CreateProcess, чтобы породить процесс С. Сначала ему нужно определить, стоит ли предоставлять процессу С доступ к своим объектам ядра. Для этого используется параметр blnberitHandles, если он приравнен TRUE, система передаст процессу С все наследуемые описатели в этом случае наследуется и описатель объекта ядра "процесс" процесса В. А вот описатель объекта "первичный поток" процесса В не наследуется ни при каком значении bInberitHandles. Кроме того, если процесс А вызывает CreateProcess, передавая через параметр blnberitHandles значение FALSE, процесс С не наследует никаких описателей, используемых в данный момент процессом А.