Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Сокровищница
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  17:11[Войти] | [Зарегистрироваться]

Перенаправление StdOut в TStream

Денис
дата публикации 21-03-2005 06:58

Перенаправление StdOut в TStream

Статья является продолжением темы о перехвате StdOut'а дочернего приложения, в частности дополнением к статье Черевко Сергея: "Перенаправление вывода консольной программы"

В исходной статье имеется недостаток - при добавлении текста в TString весь считанный блок добавляется как одна строка, что не очень удобно. Ну и к тому же мне не понравилось что процесс чтения данных из "трубы" в такой развитой среде как Delphi требует столько телодвижений, и вызовов функций Win32. Это сподвигло меня искать более другой способ чтения данных из пайпа. В конце концов я наткнулся на метод TStrings.LoadFromStream(Stream: TStream), и я подумал вот было бы неплохо одним махом залить все содержимое "трубы" сразу в Memo (ну или в любой другой TString-подобный обьект), копая дальше наткнулся на класс THandleStream, являющийся родителем класса TFileStream, и потомком TSream. Этот класс предоставляет доступ ко многим ресурсам (files, sockets, named pipes, mailslots, or other communications resources), которые предусматривают хендлы (handle).

Но не все так просто, как казалось бы. Дело в том, что дочерний процесс помещает в "трубу" не все данные, а ровно столько, сколько под "трубу" выделено, и смиренно ждет пока труба освободится. Поэтому LoadFromStream помещает в TStrings только часть данных, а добавление методом LoadFromStream не подразумевается.

И мне все-таки пришлось использовать буфер, а также для слежения "не пуст ли pipe" функцию Win32 PeekNamedPipe().

И еще есть минус — при чтении блоками, поледняя строка обрывается на середине, и приходится ее "склеивать" вручную с первой строкой следующего блока. Но все равно код получился более компактный, и прозрачный, т.к. основная часть операций с перебросом данных возложена на компоненты Borland'а (не зря же они их так тщательно разрабатывали ;)

Procedure RunAny(CommandLine: string; Str: TStrings);
var
  tRead, cWrite, dwAvail: cardinal;
  SA: TSecurityAttributes;
  PI: TProcessInformation;
  SI: TStartupInfo;
  sBuff: THandleStream;
  StringBuf: TStringList;
begin
  //Инициализация
  SA.nLength:=SizeOf(SECURITY_ATTRIBUTES);
  SA.bInheritHandle:=True;
  SA.lpSecurityDescriptor:=nil;
  if not CreatePipe(tRead, cWrite, @SA, 0) then Exit;
  ZeroMemory(@SI, SizeOf(TStartupInfo));
  SI.cb:=SizeOf(TStartupInfo);
  SI.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  SI.wShowWindow:=SW_HIDE;
  SI.hStdOutput:=cWrite;
  //Стартуем процесс...
  if CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, nil, SI, PI)
  then begin
      Str.Clear();
      sBuff := THandleStream.Create(tRead);
      StringBuf := TStringList.Create();
      repeat
        StringBuf.Clear();
        StringBuf.LoadFromStream(sBuff); //Помещаем блок в буфер
        if StringBuf.Count > 0 then
        begin
          //Склеиваем разорванную строку
          StringBuf.Strings[0] := Str.Strings[Str.Count-1]+StringBuf.Strings[0];
          Str.Delete(Str.Count-1);
        end;
        //Добавляем блок из буфера
        Str.AddStrings(StringBuf);
        //Ждем N-дцать минут
        WaitForSingleObject(PI.hProcess, 10);
        //не пуст ли pipe ?
        PeekNamedPipe(tRead, nil, 0, nil, @dwAvail, nil);
      until (dwAvail = 0);
      CloseHandle(PI.hProcess);
      CloseHandle(PI.hThread);
  end;       // if CreateProcess
  CloseHandle(tRead);
  CloseHandle(cWrite);
end;

PS: В WaitForSingleObject магическое число 10, избавиться от него не удалось с INFINITE ждет до бесконечности, буду рад если кто предложит более "прямой" вариант.




Смотрите также материалы по темам:
[Ввод/вывод (StdIn/StdOut)]

 Обсуждение материала [ 25-04-2005 11:43 ] 3 сообщения
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования