Пример программы на каналы в Windows




 

Использование anonymous pipes для перехвата StdIn/StdOut дочернего процесса.

Автор: Borland Developer Support Staff

Перевод: Валерий Вотинцев

www.исходники.ru

Тема:

 

О том, как создать дочерний процесс и передать управление его потоком ввода-вывода родительскому процессу за счет переадресации StdIn/StdOut.

Введение:

 

В настоящей статье объясняется, как запустить дочернее консольное приложение и переадресовать его стандартный ввод/вывод с использованием неименованных пайпов.

 

Неименованные пайпы используются для передачи данных только в одном направлении (только чтение или только запись). Зачем это может понадобиться? Для того, чтобы запустить какую-нибудь внешнюю утилиту и управлять ее поведением из своей программы. Например, это может быть телнет-сервер, который запускает DOS Shell и все, что выводится в шелле, передает через сокеты на удаленный хост.

 

Для начала мы поговорим о самих пайпах. Пайп в Windows - это просто один из методов коммуникации между процессами. В SDK дается следующее определение для пайпов: "пайп - это коммуникационный шлюз с двумя концами; некий процесс через дескриптор (handle) на одном конце пайпа может передавать данные другому процессу, находящемуся на другом конце пайпа."

 

В данном случае мы используем неименованные пайпы ("anonymous" pipes), т.е. однонаправленные пайпы, которые "передают данные между родительским и дочерним процессами или между двумя дочерними процессами одного и того же родительского процесса."

 

Образно говоря, пайп - это "труба", по которой между двумя процессами перетекают данные.

 

Для создания пайпа мы будем использовать функцию CreatePipe, которая вернет нам два дескриптора: дескриптор для записи и дескриптор для чтения.

 

Обратите внимание, что нам придется создать два пайпа: один для stdin и второй для stdout.

 

Далее мы будем следить за состоянием "читабельного конца" пайпа stdout для того, чтобы перехватить все, что будет там выведено, и отобразить в своем собственном окне.

 

Кроме того, мы будем проверять, введено ли что-нибудь в нашем приложении, и все, что введено, будем посылать в "записываемый конец" пайпа stdin.

Исходник:

//------------Пример использования CreateProcess и Anonymous Pipes------

 

// childspawn.cpp

// Приложение запускает shell и перехватывает его ввод/вывод

 

//---------------------use freely---------------------------------------

 

#include <windows.h>

#include <stdio.h>

#include <conio.h>

#include <string.h>

#pragma hdrstop

#include <condefs.h>

 

#define bzero(a) memset(a,0,sizeof(a)) //для сокращения писанины

 

bool IsWinNT() //проверка запуска под NT

{

OSVERSIONINFO osv;

osv.dwOSVersionInfoSize = sizeof(osv);

GetVersionEx(&osv);

return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);

}

 

void ErrorMessage(char *str) //вывод подробной информации об ошибке

{

 

LPVOID msg;

 

FormatMessage(

FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,

NULL,

GetLastError(),

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // язык по умолчанию

(LPTSTR) &msg,

0,

NULL

);

 

printf("%s: %s\n",str,msg);

LocalFree(msg);

 

}

 

//----------------------------------------------------------------------

 

void main()

 

{

 

char buf[1024]; //буфер ввода/вывода

 

STARTUPINFO si;

SECURITY_ATTRIBUTES sa;

SECURITY_DESCRIPTOR sd; //структура security для пайпов

PROCESS_INFORMATION pi;

 

HANDLE newstdin,newstdout,read_stdout,write_stdin; //дескрипторы

// пайпов

 

 

if (IsWinNT()) //инициализация security для Windows NT

{

InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);

SetSecurityDescriptorDacl(&sd, true, NULL, false);

sa.lpSecurityDescriptor = &sd;

}

 

else sa.lpSecurityDescriptor = NULL;

 

sa.nLength = sizeof(SECURITY_ATTRIBUTES);

sa.bInheritHandle = true; //разрешаем наследование дескрипторов

 

 

if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) //создаем пайп

// для stdin

{

ErrorMessage("CreatePipe");

getch();

return;

}

 

if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) //создаем пайп

// для stdout

{

ErrorMessage("CreatePipe");

getch();

CloseHandle(newstdin);

CloseHandle(write_stdin);

return;

}

 

GetStartupInfo(&si); //создаем startupinfo для

// дочернего процесса

 

/*

 

Параметр dwFlags сообщает функции CreateProcess

как именно надо создать процесс.

 

STARTF_USESTDHANDLES управляет полями hStd*.

STARTF_USESHOWWINDOW управляет полем wShowWindow.

 

*/

 

si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;

si.wShowWindow = SW_HIDE;

si.hStdOutput = newstdout;

si.hStdError = newstdout; //подменяем дескрипторы для

si.hStdInput = newstdin; // дочернего процесса

 

char app_spawn[] = "d:\\winnt\\system32\\cmd.exe"; //это просто

// пример,

//замените на то,

// что вам нужно

 

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

 

if (!CreateProcess(app_spawn,NULL,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,

NULL,NULL,&si,&pi))

{

ErrorMessage("CreateProcess");

getch();

CloseHandle(newstdin);

CloseHandle(newstdout);

CloseHandle(read_stdout);

CloseHandle(write_stdin);

return;

}

 

unsigned long exit=0; //код завершения процесса

unsigned long bread; //кол-во прочитанных байт

unsigned long avail; //кол-во доступных байт

 

bzero(buf);

 

for(;;) //основной цикл программы

{

GetExitCodeProcess(pi.hProcess,&exit); //пока дочерний процесс

// не закрыт

if (exit!= STILL_ACTIVE)

break;

 

PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL);

 

//Проверяем, есть ли данные для чтения в stdout

 

if (bread!= 0)

{

bzero(buf);

if (avail > 1023)

{

while (bread >= 1023)

{

ReadFile(read_stdout,buf,1023,&bread,NULL); //читаем из

// пайпа stdout

printf("%s",buf);

bzero(buf);

}

}

 

else {

ReadFile(read_stdout,buf,1023,&bread,NULL);

printf("%s",buf);

}

}

 

if (kbhit()) //проверяем, введено ли что-нибудь с клавиатуры

{

bzero(buf);

*buf = (char)getche();

 

//printf("%c",*buf);

 

WriteFile(write_stdin,buf,1,&bread,NULL); //отправляем это

// в stdin

 

if (*buf == '\r') {

*buf = '\n';

printf("%c",*buf);

WriteFile(write_stdin,buf,1,&bread,NULL); //формирум конец

//строки, если нужно

 

}

}

}

 

CloseHandle(pi.hThread);

CloseHandle(pi.hProcess);

CloseHandle(newstdin); //небольшая уборка за собой

CloseHandle(newstdout);

CloseHandle(read_stdout);

CloseHandle(write_stdin);

}

 

Практическое занятие 8.

 

Мы переходим к рассмотрению самого важного и универсального типа сокетов – сетевых сокетов. Думаю, что о значении, которое имеют сетевые сокеты в Unix-системах, распространяться не нужно. Даже если вы пишете систему приложений, предназначенных для работы на одном компьютере, рассмотрите возможность использования сетевых сокетов для обмена данными между этими приложениями. Возможно, в будущем ваш программный комплекс наберет мощь и возникнет необходимость распределить его компоненты на нескольких машинах.

 

Использование сетевых сокетов сделает процесс масштабирования проекта безболезненным. Впрочем, у сетевых сокетов есть и недостатки. Даже если сокеты используются для обмена данными на одной и той же машине, передаваемые данные должны пройти все уровни сетевого стека, что отрицательно сказывается на быстродействии и нагрузке на систему.

 

В качестве примера мы рассмотрим комплекс из двух приложений, клиента и сервера, использующих сетевые сокеты для обмена данными. Текст программы сервера вы найдете в файле netserver.c, ниже мы приводим некоторые фрагменты. Прежде всего, мы должны получить дескриптор сокета:

sock = socket(AF_INET, SOCK_STREAM, 0);

if (socket < 0) {

printf("socket() failed: %d\n", errno);

return EXIT_FAILURE;

}

 

В первом параметре функции socket() мы передаем константу AF_INET, указывающую на то, что открываемый сокет должен быть сетевым. Значение второго параметра требует, чтобы сокет был потоковым. Далее мы, как и в случае сокета в файловом пространстве имен, вызываем функцию bind():

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = INADDR_ANY;

serv_addr.sin_port = htons(port);

if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {

printf("bind() failed: %d\n", errno);

return EXIT_FAILURE;

}

 

Переменная serv_addr, - это структура типа sockaddr_in. Тип sockaddr_in специально предназначен для хранения адресов в формате Интернета. Самое главное отличие sockaddr_in от sockaddr_un – наличие параметра sin_port, предназначенного для хранения значения порта. Функция htons() переписывает двухбайтовое значение порта так, чтобы порядок байтов соответствовал принятому в Интернете (см. врезку). В качестве семейства адресов мы указываем AF_INET (семейство адресов Интернета), а в качестве самого адреса – специальную константу INADDR_ANY. Благодаря этой константе наша программа сервер зарегистрируется на всех адресах той машины, на которой она выполняется.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: