RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

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

Однажды мне понадобилось передать информацию на stdin внешней программы, которую я запускал из своей. Долго ли, коротко ли изыскивал я информацию в хелпах, но в конце концов нашел письмо в эхе SU.OS2.PROG (автор Anatoly Belankov 2:5054/2.31), которое так и называлось "Как правильно дупить std*". Кое-что пришлось поправить, добавить комментарии, получилась простенькая тестовая программа, демонстрирующая принцип перенаправления стандартных потоков ввода/вывода.

Идея проста: стандартные потоки ввода/вывода 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 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов
(C) Russian Underground/2