| The Russian Electronic Developer Magazine | |
| Русский электронный журнал разработчика | |
Идея проста: стандартные потоки ввода/вывода stdin, stdout, stderr перенаправляются на pipes, т.е. чтобы записать данные на stdin или stdout, достаточно записать или считать данные соответствующего pipe. Далее запускается внешняя программа. Теперь мы можем записать в один pipe информацию, которая попадает на stdin порожденной программе. Из другого pipe считываем информацию, которую порожденная программа выдает на stdout и stderr.
Основной принцип понятен, остальное можно найти в комментариях в тексте программы.
//
// Тестовый пример как можно вызвать внешнюю программу,
// затем передать ей на stdin данные и получить stdout, stderr
// этой программы.
//
// Компиляция с помощью Watcom C/C++:
// wcl386 dupstd.cpp
//
#define INCL_DOSQUEUES
#define INCL_DOSFILEMGR
#define INCL_DOSNMPIPES
#define INCL_DOSERRORS
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Хандлы стандартных потоков ввода/вывода
#define STD_IN 0
#define STD_OUT 1
#define STD_ERR 2
// Размер буфера для pipes
#define PIPESIZE 4096
// Хандлы для pipes
HPIPE
PipeReadHandle1, PipeWriteHandle1,
PipeReadHandle2, PipeWriteHandle2;
// В этих переменных мы сохраним хандлы (сдублированные) стандартных потоков.
// !!! Инициализация в -1 необходима (см. описание функции DosDupHandle),
// чтобы хандлы стандартных потоков отобразить на новые хандлы, которые
// потом понадобятся для восстановления стандартных хандлов обратно.
// Т.е. если эти хандлы не восстановить, Ваша программа останется
// "глухой" и "слепой", например для стандартной фунцкии printf().
HFILE
SavedInputHandle =-1,
SavedOutputHandle =-1,
SavedErrorsHandle =-1;
// Буфер для ввода/вывода в pipes
CHAR buf[PIPESIZE];
//
// Собственно процедура, которая выполняет перенаправление
// и запускает внешнюю программу из буфера progName.
//
void runService(const CHAR *progName) {
// Хандлы стандартных потоков
HFILE
NewHandleStdIn = STD_IN,
NewHandleStdOut = STD_OUT,
NewHandleStdErr = STD_ERR;
// Создаем копии стандартных потоков
// Здесь главный момент в том, что стандартные потоки
// получают новые нестандартные хандлы, (их, собственно,
// можно использовать для вывода данных на stdout в
// родительской программе, см. ниже)
DosDupHandle(NewHandleStdIn, &SavedInputHandle);
DosDupHandle(NewHandleStdOut, &SavedOutputHandle);
DosDupHandle(NewHandleStdErr, &SavedErrorsHandle);
// Создаем pipes для связи в внешней программой.
// В данном примере можно использовать только один pipe,
// но я использую два, не принципиально.
DosCreatePipe(&PipeReadHandle1, &PipeWriteHandle1, PIPESIZE);
DosCreatePipe(&PipeReadHandle2, &PipeWriteHandle2, PIPESIZE);
// PipeReadHandle1 - будет дублироваться на stdin внешней
// программы, т.е. записывая любые данные в PipeWriteHandle1,
// мы передаем эти данные на stdin внешней программы
DosDupHandle(PipeReadHandle1, &NewHandleStdIn);
// PipeWriteHandle1 - будет дублироваться на stdout внешней
// программы, т.е. все записанные на stdout данные,
// можно прочитать через PipeReadHandle2
DosDupHandle(PipeWriteHandle2, &NewHandleStdOut);
// stderr и stdout для прстоты выводятся на один хандл (впрочем
// никто не мешает создать еще pipe и воспользоваться им)
DosDupHandle(NewHandleStdOut, &NewHandleStdErr);
// В буфер записываем команды, передаваемые на stdin cmd.exe
// (либо Вашей внешней программы). Здесь это команда dir,
// с последующей командой exit, для выхода из cmd.exe
strcpy(buf,"dir\nexit\n");
// Записываем данные буфера на PipeWriteHandle1,
// т.е. на stdin внешней программы!
ULONG btWrited;
DosWrite(PipeWriteHandle1,buf,strlen(buf),&btWrited);
// Этот текст чисто в тестовых целях выводим на stdout
// нашей программы (родительской)
CHAR *test = "Это выводится на экран родительской программы\n\r";
DosWrite(SavedOutputHandle,test,strlen(test),&btWrited);
// Перенаправление сделано, можно вывести данные на экран вызываемой
// программы.
printf("Эта строка выводится на экран в запускаемую программу, на stdout\n");
// Здесь я использую system для вызова внешней программы только
// для простоты описания примера, Вы, возможно, будете использовать
// другие функции (например DosExecPgm)
system(progName);
// ! Мы вызвали внешнюю программы, заранее записав в буфер ввода
// ! команды dir и exit, т.е. внешняя программа (cmd.exe) выполнит
// ! команду dir, выведет в pipe информацию, затем завершится,
// ! выполнив команду exit. Выведенная в pipe информация готова
// ! для считывания. Что мы сейчас и сделаем.
// Читаем данные, выведенные на stdout командой dir
ULONG btReaded;
DosRead(PipeReadHandle2,buf,PIPESIZE-1,&btReaded);
if(btReaded>0) buf[btReaded] = 0;
// Сброс буферов ввода/вывода
DosResetBuffer(STD_IN);
DosResetBuffer(STD_OUT);
DosResetBuffer(STD_ERR);
// Теперь восстанавливаем стандартные stdin, stdout, stderr
// которые мы сохранили в начале процедуры
DosDupHandle(SavedInputHandle, &NewHandleStdIn);
DosDupHandle(SavedOutputHandle, &NewHandleStdOut);
DosDupHandle(SavedErrorsHandle, &NewHandleStdErr);
// Закрываем пайпы, они больше не нужны
DosClose(PipeReadHandle1);
DosClose(PipeWriteHandle1);
DosClose(PipeWriteHandle2);
DosClose(PipeReadHandle2);
// Выводим на stdout информацию, которую мы получили из
// внешней программы.
printf("Эти данные мы считали из pipe:\n%s\n",buf);
}
void main()
{
runService("cmd.exe");
}
Vladimir Kiselev KiSoft, 1999
Интересные ссылки:
Комментариев к странице: 0 | Добавить комментарий
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов