Создание дочернего процесса с переназначенным вводом и выводом

Пример в этом разделе демонстрирует, как создать дочерний процесс из консольного процесса. Он также демонстрирует методику использования неименованных программных каналов, чтобы переназначить стандартные дескрипторы ввода и вывода дочернего процесса.

Функция CreatePipe использует структуру SECURITY_ATTRIBUTES, чтобы создать наследуемые дескрипторы для чтения и записи в концах двух каналов. Конец чтения в одном из каналов служит как стандартный ввод данных для дочернего процесса, и конец записи в другом канале является стандартным выводом для дочернего процесса. Эти дескрипторы каналов устанавливаются в функции SetStdHandle, которая делает их стандартными дескрипторами, унаследованными дочерним процессом. После того, как дочерний процесс создан, SetStdHandle используется снова, чтобы восстановить стандартные исходные дескрипторы для родительского процесса.

Родительский процесс использует другие концы каналов, чтобы записать введенные данные и прочитать вывод данных дочернего процесса. Дескрипторы этих концов канала также наследуемы. Однако дескриптор не должен быть унаследован. Перед созданием дочернего процесса, родительский процесс должен использовать DuplicateHandle, чтобы создать дубликат определяемой программой глобальной переменной hChildStdinWr, которая не может быть унаследована. Тогда он использует CloseHandle, чтобы закрыть наследуемый дескриптор. Для получения дополнительной информации, см. главу Каналы.

Нижеследующее - родительский процесс.

#include <stdio.h>

#include <windows.h>

#define BUFSIZE 4096

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,

hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,

hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID);

VOID WriteToPipe(VOID);

VOID ReadFromPipe(VOID);

VOID ErrorExit(LPTSTR);

VOID ErrMsg(LPTSTR, BOOL);

DWORD main(int argc, char *argv[])

{

SECURITY_ATTRIBUTES saAttr;

BOOL fSuccess;

// Установим флажок bInheritHandle, так как дескрипторы канала наследуются.

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

saAttr.bInheritHandle = TRUE;

saAttr.lpSecurityDescriptor = NULL;

// Шаги для переназначения STDOUT дочернего процесса

// 1.Сохраним текущий STDOUT, который позже восстановим.

// 2. Создадим анонимный канал, который будет STDOUT дочернего процесса.

// 3. Установим STDOUT родительского процесса, который будет,

// дескриптором записи канала наследуемым дочерним процессом.

// 4. Создадим ненаследуемый дубликат дескриптора чтения и закроем

// наследуемый дескриптор чтения.

// Сохраним дескриптор текущего STDOUT.

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

// Создадим канал для STDOUT дочернего процесса.

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))

ErrorExit("Stdout pipe creation failed\n");

// Установим дескриптор записи канала, который будет STDOUT.

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))

ErrorExit("Redirecting STDOUT failed");

 

// Создадим ненаследуемый дескриптор чтения и закроем наследуемый дескриптор чтения.

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,

GetCurrentProcess(), &hChildStdoutRdDup , 0,

FALSE,

DUPLICATE_SAME_ACCESS);

if( !fSuccess )

ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdoutRd);

// Шаги для переназначения STDIN дочернего процесса:

// 1.Сохраним текущий STDIN, который позже восстановим.

// 2. Создадим анонимный канал, который будет STDIN дочернего процесса.

// 3. Установим STDIN родительского процесса, который будет дескриптором

// чтения канала, так чтобы он наследовался дочерним процессом.

// 4. Создадим ненаследуемый дубликат дескриптора записи закроем

// наследуемый дескриптор записи.

// Сохраним дескриптор текущего STDIN.

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

// Создадим канал для STDIN дочернего процесса.

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))

ErrorExit("Stdin pipe creation failed\n");

// Установим дескриптор чтения для канала, который будет STDIN.

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))

ErrorExit("Redirecting Stdin failed");

// Продублируем дескриптор записи для канала, так что он не был унаследован.

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,

GetCurrentProcess(), &hChildStdinWrDup, 0,

FALSE, // не наследуется

DUPLICATE_SAME_ACCESS);

if (! fSuccess)

ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdinWr);

// Теперь создадим дочерний процесс.

if (! CreateChildProcess())

ErrorExit("Create process failed");

// После создания процесса, восстановим сохраненные STDIN и STDOUT.

if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))

ErrorExit("Re-redirecting Stdin failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))

ErrorExit("Re-redirecting Stdout failed\n");

// Получим дескриптор для входного файла родителя.

if (argc > 1)

hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

else

hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)

ErrorExit("no input file\n");

// Запишем в канал, который является стандартным вводом данных для дочернего

// процесса.

WriteToPipe();

// Прочитаем из канала, который является стандартным выводом для дочернего

// процесса.

ReadFromPipe();

return 0;

}

BOOL CreateChildProcess()

{

PROCESS_INFORMATION piProcInfo;

STARTUPINFO siStartInfo;

// Установим член структуры PROCESS_INFORMATION.

ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

// Установим член структуры STARTUPINFO.

ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );

siStartInfo.cb = sizeof(STARTUPINFO);

// Создадим дочерний процесс.

return CreateProcess(NULL,

"child", // командная строка

NULL, // атрибуты защиты процесса

NULL, // атрибуты защиты первичного потока

TRUE, // дескрипторы наследуемые

0, // флажки создания

NULL, // использование конфигурации родителя

NULL, // использование текущего каталога родителя

&siStartInfo, // указатель на STARTUPINFO

&piProcInfo); // принимаем PROCESS_INFORMATION

}

VOID WriteToPipe(VOID)

{

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

// Чтение из файла и запись его контекста в канал.

for (;;)

{

if (! ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ||

dwRead == 0) break;

if (! WriteFile(hChildStdinWrDup, chBuf, dwRead,

&dwWritten, NULL)) break;

}

// Закрываем дескриптор канала, так чтобы дочерний процесс остановил чтение.

if (! CloseHandle(hChildStdinWrDup))

ErrorExit("Close pipe failed\n");

}

VOID ReadFromPipe(VOID)

{

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

// Закрываем конец записи канала перед чтением из конца чтения канала.

if (!CloseHandle(hChildStdoutWr))

ErrorExit("Closing handle failed");

// Читаем выводимую информацию дочерним процессом, и записываем в STDOUT

// родителя.

for (;;)

{

if( !ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead,

NULL) || dwRead == 0) break;

if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))

break;

}

}

VOID ErrorExit (LPTSTR lpszMessage)

{

fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);

}

// Код для дочернего процесса.

#include <windows.h>

#define BUFSIZE 4096

VOID main(VOID)

{

CHAR chBuf[BUFSIZE];

DWORD dwRead, dwWritten;

HANDLE hStdin, hStdout;

BOOL fSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

hStdin = GetStdHandle(STD_INPUT_HANDLE);

if ((hStdout == INVALID_HANDLE_VALUE) ||

(hStdin == INVALID_HANDLE_VALUE))

ExitProcess(1);

for (;;)

{

// Читаем из стандартного ввода данных.

fSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

if (! fSuccess || dwRead == 0)

break;

// Записываем в стандартный вывод данных

fSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

if (! fSuccess)

break;

}

}

 

Назад в оглавление темы
На главную страницу темы

Hosted by uCoz